iOS には、階層構造を表現・選択する UI コントロールが提供されていません。
検索すると、Xamarin iOS – Create custom TreeView control for iPad / iPhone « Milen's Blog で実装しているコードがありましたが、きちんと動作していない感じだったので、作って見ました。コードは、diva-osaka/xamarin-ios-treeviewsample にあります。
フォルダーを閉じると、これまで開いていた情報は忘れます(再度 開くと直下の子要素しか表示しません)。
ノートを表すクラス。とりあえず表示と動作に必要なプロパティしかありません。
using System.Collections.Generic; namespace TreeViewSample { public class TreeNode { public List<TreeNode> Children { get; set; } = new List<TreeNode>(); public string Name { get; set; } public int Level { get; set; } public bool IsExpanded { get; set; } } }
UITableViewController を元に作成。
using Foundation; using System; using System.Collections.Generic; using System.Linq; using UIKit; namespace TreeViewSample { public partial class FolderViewController : UITableViewController { public FolderViewController(IntPtr handle) : base(handle) { } public override void ViewDidLoad() { base.ViewDidLoad(); TableView.RowHeight = FolderCell.Height; TableView.SeparatorColor = UIColor.Clear; TableView.Source = new TreeViewSource(CreateNodes()); } private TreeNode CreateNodes() { var osaka = new TreeNode { Level = 0, Name = "大阪市" }; var sub1 = new TreeNode { Level = 1, Name = "中央区" }; var sub2 = new TreeNode { Level = 1, Name = "北区" }; var sub3 = new TreeNode { Level = 1, Name = "西区" }; osaka.Children.Add(sub1); osaka.Children.Add(sub2); osaka.Children.Add(sub3); var sub4 = new TreeNode { Level = 2, Name = "淡路町" }; sub1.Children.Add(sub4); return osaka; } private class TreeViewSource : UITableViewSource { private List<TreeNode> Nodes = new List<TreeNode>(); public TreeViewSource(TreeNode root) { Nodes.Add(root); } public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) { var cell = tableView.DequeueReusableCell("FolderCell") as FolderCell; cell.SetCellContents(Nodes[indexPath.Row]); return cell; } public override nint RowsInSection(UITableView tableview, nint section) { return Nodes.Count; } public override void RowSelected(UITableView tableView, NSIndexPath indexPath) { var selectedNode = Nodes[indexPath.Row]; var selectedIndex = indexPath.Row; if (!selectedNode.IsExpanded) { var children = selectedNode.Children; // 開く if (!children.Any()) { return; } selectedNode.IsExpanded = true; var indexPaths = new List<NSIndexPath>(); foreach (var node in children.Select((Value, Index) => new { Value, Index })) { node.Value.IsExpanded = false; indexPaths.Add(NSIndexPath.FromRowSection(selectedIndex + node.Index + 1, 0)); } Nodes.InsertRange(selectedIndex + 1, children); tableView.InsertRows(indexPaths.ToArray(), UITableViewRowAnimation.Automatic); } else { // 閉じる selectedNode.IsExpanded = false; var node = Nodes.Skip(selectedIndex + 1).FirstOrDefault(i => i.Level <= selectedNode.Level); var deleteCount = (node != null) ? Nodes.IndexOf(node) - selectedIndex - 1 : Nodes.Count - selectedIndex - 1; var indexPaths = new List<NSIndexPath>(); for (int i = 0; i < deleteCount; i++) { Nodes.RemoveAt(selectedIndex + 1); tableView.DeleteRows(new NSIndexPath[] { NSIndexPath.FromRowSection(selectedIndex + 1, 0) }, UITableViewRowAnimation.Top); } } } } } }
using CoreGraphics; using Foundation; using System; using UIKit; namespace TreeViewSample { [Register("FolderCell")] public class FolderCell : UITableViewCell { static readonly public int Height = 52; private int level; private UIImageView imageView; private UILabel titleLabel; public FolderCell(IntPtr handle) : base(handle) { } void ReleaseDesignerOutlets() { } public void SetCellContents(TreeNode node) { level = node.Level; imageView = new UIImageView { Image = UIImage.FromFile("Folder.png"), ContentMode = UIViewContentMode.Left }; imageView.SizeToFit(); titleLabel = new UILabel() { TextColor = UIColor.Black, BackgroundColor = UIColor.Clear, Text = node.Name }; titleLabel.SizeToFit(); foreach (var v in ContentView.Subviews) { v.RemoveFromSuperview(); } ContentView.AddSubviews(imageView, titleLabel); } public override void LayoutSubviews() { base.LayoutSubviews(); var indent = 28; var margin = 8; var imageFrame = new CGRect(level * indent + margin, 0, 28.0f, Height); imageView.Frame = imageFrame; var titleFrame = new CGRect(level * indent + imageFrame.Width + margin * 2, 0, (float)titleLabel.Bounds.Width, Height); titleLabel.Frame = titleFrame; } } }