Skip to content

Instantly share code, notes, and snippets.

@redent
Created September 12, 2014 09:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save redent/e76db2f2af7634654bb8 to your computer and use it in GitHub Desktop.
Save redent/e76db2f2af7634654bb8 to your computer and use it in GitHub Desktop.
Xamarin sample of collapsable sections using MvvmCross
using System;
using MonoTouch.UIKit;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using MonoTouch.Foundation;
using Cirrious.MvvmCross.Binding.ExtensionMethods;
namespace TwinCoders.MvvmCross.TouchUtils.TableViews
{
public class CollapsableSectionTableViewSource : MultiSectionTableViewSource
{
public abstract class CollapsableHeader: UIView
{
public abstract event EventHandler TouchUpInside;
}
public override IEnumerable ItemsSource
{
get { return base.ItemsSource; }
set
{
if (CollapseSectionsByDefault && value != null)
_collapsedSections = value.Cast<object>().ToList();
base.ItemsSource = value;
}
}
public bool CollapseSectionsByDefault { get; set; }
public delegate CollapsableHeader CreateHeaderDelegate(UITableView tableView, object sectionElement, int sectionIndex, bool collapsed);
public CreateHeaderDelegate CreateHeader { get; set; }
private IList _collapsedSections = new List<object>();
public CollapsableSectionTableViewSource(UITableView tableView, NSString cellIdentifier) : base(tableView, cellIdentifier)
{
}
public override UIView GetViewForHeader(UITableView tableView, int section)
{
if (CreateHeader == null)
return null;
var sectionElement = ItemsSource.ElementAt(section);
var isCollapsed = _collapsedSections.Contains(sectionElement);
var header = CreateHeader(tableView, sectionElement, section, isCollapsed);
header.TouchUpInside += (sender, args) => ToggleCollapseSection(section);
return header;
}
public override int RowsInSection(UITableView tableview, int section)
{
var rows = base.RowsInSection(tableview, section);
var sectionElement = ItemsSource.ElementAt(section);
if (_collapsedSections.Contains(sectionElement))
rows = 0;
return rows;
}
public void SetSectionCollapsed(int section, bool collapsed, bool animated = true) {
var sectionElement = ItemsSource.ElementAt(section);
if (sectionElement == null)
return;
if (collapsed)
_collapsedSections.Add(sectionElement);
else
_collapsedSections.Remove(sectionElement);
if (UseAnimations && animated)
TableView.ReloadSections(new NSIndexSet((uint)section), UITableViewRowAnimation.Automatic);
else
ReloadTableData();
}
public void ToggleCollapseSection(int section) {
var sectionElement = ItemsSource.ElementAt(section);
if (sectionElement == null)
return;
var isCollapsed = _collapsedSections.Contains(sectionElement);
SetSectionCollapsed(section, !isCollapsed);
}
}
}
using System;
using Cirrious.MvvmCross.Binding.Touch.Views;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Linq;
namespace TwinCoders.MvvmCross.TouchUtils.TableViews
{
public class GenericMvxTableViewSource : MvxTableViewSource, ITableViewEventSource
{
public delegate MvxTableViewCell CreateCellDelegate(UITableView tableView, NSIndexPath indexPath, object item);
public delegate void CellModifierDelegate(UITableViewCell cell, NSIndexPath indexPath, object item);
protected virtual NSString CellIdentifier { get; private set; }
public CreateCellDelegate CreateCell { get; set; }
public CellModifierDelegate CellModifier { get; set; }
public GenericMvxTableViewSource(UITableView tableView, NSString cellIdentifier) : base(tableView)
{
CellIdentifier = cellIdentifier;
}
protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
{
var reuse = tableView.DequeueReusableCell(CellIdentifier);
var cell = reuse ?? CreateDefaultBindableCell(tableView, indexPath, item);
if (CellModifier != null)
CellModifier(cell, indexPath, item);
return cell;
}
protected virtual MvxTableViewCell CreateDefaultBindableCell(UITableView tableView, NSIndexPath indexPath, object item)
{
return CreateCell != null ? CreateCell(tableView, indexPath, item) : null;
}
public new object SelectedItem {
get { return base.SelectedItem; }
set
{
base.SelectedItem = value;
if (value != null)
{
var index = IndexForElement(value);
if (index != null)
TableView.SelectRow(index, false, UITableViewScrollPosition.None);
}
else
{
var index = TableView.IndexPathForSelectedRow;
if (index != null && index.Row >= 0)
TableView.DeselectRow(index, false);
}
}
}
public virtual NSIndexPath IndexForElement(object element) {
if (ItemsSource == null || element == null)
return null;
return NSIndexPath.FromRowSection(ItemsSource.Cast<object>().ToList().IndexOf(element), 0);
}
#region ITableViewEventSource implementation
public event EventHandler OnDecelerationEnded;
public event EventHandler OnDecelerationStarted;
public event EventHandler OnDidZoom;
public event EventHandler OnDraggingStarted;
public event EventHandler OnScrollAnimationEnded;
public event EventHandler OnScrolled;
public event EventHandler OnScrolledToTop;
public override void DecelerationEnded(UIScrollView scrollView)
{
if (OnDecelerationEnded != null)
OnDecelerationEnded(scrollView, null);
}
public override void DecelerationStarted(UIScrollView scrollView)
{
if (OnDecelerationStarted != null)
OnDecelerationStarted(scrollView, null);
}
public override void DidZoom(UIScrollView scrollView)
{
if (OnDidZoom != null)
OnDidZoom(scrollView, null);
}
public override void DraggingStarted(UIScrollView scrollView)
{
if (OnDraggingStarted != null)
OnDraggingStarted(scrollView, null);
}
public override void ScrollAnimationEnded(UIScrollView scrollView)
{
if (OnScrollAnimationEnded != null)
OnScrollAnimationEnded(scrollView, null);
}
public override void Scrolled(UIScrollView scrollView)
{
if (OnScrolled != null)
OnScrolled(scrollView, null);
}
public override void ScrolledToTop(UIScrollView scrollView)
{
if (OnScrolledToTop != null)
OnScrolledToTop(scrollView, null);
}
#endregion
}
}
using System;
using Cirrious.MvvmCross.Binding.Touch.Views;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Collections.Generic;
using System.Collections;
using Cirrious.MvvmCross.Binding.ExtensionMethods;
using System.Linq;
namespace TwinCoders.MvvmCross.TouchUtils.TableViews
{
public class MultiSectionTableViewSource : GenericMvxTableViewSource
{
public delegate IList GetElementsFromSectionDelegate(object section);
public delegate UIView HeaderDelegate(UITableView tableView, int section, object sectionItem);
public GetElementsFromSectionDelegate GetElementsFromSection { get; set; }
public HeaderDelegate CreateHeader { get; set; }
public HeaderDelegate CreateFooter { get; set; }
public MultiSectionTableViewSource(UITableView tableView, NSString cellIdentifier) : base(tableView, cellIdentifier)
{
}
protected IList GetElements(object section)
{
var elements = section as IList;
if (GetElementsFromSection != null)
elements = GetElementsFromSection(section);
return elements;
}
protected override object GetItemAt(NSIndexPath indexPath)
{
return ItemsSource == null ? null : GetElements(ItemsSource.ElementAt(indexPath.Section)).ElementAt(indexPath.Row);
}
public override int NumberOfSections(UITableView tableView)
{
return ItemsSource == null ? 0 : ItemsSource.Count();
}
public override int RowsInSection(UITableView tableview, int section)
{
return ItemsSource == null ? 0 : GetElements(ItemsSource.ElementAt(section)).Count;
}
public override UIView GetViewForHeader(UITableView tableView, int section) {
if (CreateHeader == null)
return null;
var sectionItem = ItemsSource.Cast<object>().ElementAt(section);
return CreateHeader(tableView, section, sectionItem);
}
public override UIView GetViewForFooter(UITableView tableView, int section) {
if (CreateFooter == null)
return null;
var sectionItem = ItemsSource.Cast<object>().ElementAt(section);
return CreateFooter(tableView, section, sectionItem);
}
}
}
@java13
Copy link

java13 commented Apr 13, 2015

Had some crashes with an app built on iOS7.1 when running on iOS8.
Looks like that UITableViewHeaderFooterView inner workings has changed in iOS8 (see #148), which resulted in assertions errors in NSObject+PXSubclass.m:63 :

if (![object isKindOfClass:[self superclass]]) {
NSAssert(NO, @"Class %@ doesn't fit for subclassing.", [superClass description]);
return;
}

Any idea?

Copy link

ghost commented Sep 16, 2015

This line: TableView.ReloadSections(new NSIndexSet((uint)section), UITableViewRowAnimation.Automatic);
Crashes my app only on iOS 9, any ideas how to fix it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment