I'm using mono develop 3.1.1 to build an IOS application. I'm receiving an object reference error (see >>>) from my reference to the navigation controller that I've not declared properly.
My question is what's the best way to declare and instantiate the controller so I'm able to show another view from the point the table cell is selected.
Can someone help me with the correct syntax please?
public class TableHelper : UITableViewSource {
protected string[] tableItems;
protected string cellIdentifier = "TableCell";
public TableHelper (string[] items)
{
tableItems = items;
}
public override int RowsInSection (UITableView tableview, int section)
{
return tableItems.Length;
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
switch (tableItems[indexPath.Row])
{
case "one":
var DetailViewController = new SupportContactsDetailsScreen ();
UINavigationController controller = new UINavigationController();
// Pass the selected object to the new view controller.
>>>controller.NavigationController.PushViewController(DetailViewController, true);
break;
default:
//Console.WriteLine("Default case");
break;
}
}
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier);
if (cell == null)
cell = new UITableViewCell (UITableViewCellStyle.Default, cellIdentifier);
cell.TextLabel.Text = tableItems[indexPath.Row];
return cell;
}
}
The way that I usually go about doing this is to keep a reference to the main UIViewController (the view controller that holds the UITableView) for that particular set of views and access that Navigation Controller through the NavigationController property. (Another technique taken by Xamarin in the code sample linked below is to pass the UINavigationController in directly.)
So I would alter your class by adding:
UIViewController parentViewController;
public TableHelper(string[] items, UIViewController vc)
{
tableItems = items;
parentViewController vc;
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
switch (tableItems[indexPath.Row])
{
case "one":
var DetailViewController = new SupportContactsDetailsScreen ();
UINavigationController controller = new UINavigationController();
// Pass the selected object to the new view controller.
parentViewController.NavigationController.PushViewController(DetailViewController, true);
break;
default:
//Console.WriteLine("Default case");
break;
}
}
Xamarin has a document on their docs site as well as some code on their Github that talks about this further. Another important note is what the type of view controller is (regular UIViewController, UITableViewController, etc.).
Related
I'm wondering how can I get the relative position of a cell in a collection view in regards to the app window? I saw some examples for swift and forms, but i can't find any for Xamarin.iOS
Translate the solution in this thread:
public class collectionViewDelegate : UICollectionViewDelegate {
UIView view;
public collectionViewDelegate(UIView v)
{
//get the parent v when create collectionViewDelegate instance
this.view = v;
}
public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath)
{
UICollectionViewCell cell = (UICollectionViewCell)collectionView.DequeueReusableCell("CollectionViewCell",indexPath);
CGRect frame = new CGRect(0,0,cell.Frame.Size.Width,cell.Frame.Size.Height);
frame.Location = collectionView.ConvertPointToView(cell.Frame.Location, this.view);
}
}
This question is related to Xamarin.iOS. I have a UICollectionView and a Label inside a UIViewController. I have set up source for UICollectionView. In it I override the methods ItemSelected and ItemDeselected to highlight the selected items. I also get the index of the selected cell in the UICollectionView. But I don't know how pass this index to parent UIViewController so I can update the label based on the selected cell in UICollectionView.
public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = (newCustomCollectionViewCell)collectionView.CellForItem(indexPath);
//cell.mainLabel.Alpha = 0.5f;
cell.ImageView.Alpha = 0.9f;
cell.ImageView.Layer.BorderWidth = 3.0f;
cell.ImageView.Layer.BorderColor = UIColor.Red.CGColor;
//base.ItemSelected(collectionView, indexPath);
_selectedItems.Add(indexPath);
ViewController.selectedIndex = indexPath.ToString();
ViewController.cusLabel.Text = indexPath.ToString();
//Console.WriteLine("Index of the highlighted cell is {0}", indexPath);
}
public override void ItemDeselected(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = (newCustomCollectionViewCell)collectionView.CellForItem(indexPath);
//cell.mainLabel.Alpha = 0.5f;
cell.ImageView.Alpha = 1f;
cell.ImageView.Layer.BorderWidth = 3.0f;
cell.ImageView.Layer.BorderColor = UIColor.White.CGColor;
//base.ItemDeselected(collectionView, indexPath);
_selectedItems.Remove(indexPath);
}
The above code is in newCustomCollectionSource.cs
I want to access the index of the cell in the parent view controller UIViewController
public override void ViewDidLoad()
{
base.ViewDidLoad();
cusLabel.Frame = new CGRect(50, 50, 100, 30);
cusLabel.BackgroundColor = UIColor.Gray;
CustomColllectionView.RegisterClassForCell(typeof(newCustomCollectionViewCell), newCustomCollectionViewCell.CellID);
CustomColllectionView.Source = new newCustomCollectionSource(imageLocations);
}
You can firstly declare a member variable in the your ViewController
public NSIndexPath selectedIndex ;
Then ,there two ways that you can pass this index to parent UIViewController .
Firstly ,you can implement the method of the UICollectionViewDataSource and UICollectionViewDelegate in the ViewController. And set the value of the member when you select the item .Refer to the following code:
public partial class ViewController : UIViewController,IUICollectionViewDataSource,IUICollectionViewDelegate
. . .
public override void ViewDidLoad()
{
base.ViewDidLoad();
cusLabel.Frame = new CGRect(50, 50, 100, 30);
cusLabel.BackgroundColor = UIColor.Gray;
CustomColllectionView.RegisterClassForCell(typeof(newCustomCollectionViewCell), newCustomCollectionViewCell.CellID);
CustomColllectionView.WeakDataSource= this;
}
public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath)
{
…
this. myIndexPath= NSIndexPath.FromRowSection(indexPath.Row, indexPath. Section);
}
If you do want to implement the method of the UICollectionViewDataSource and UICollectionViewDelegate in single class. Pass the your ViewController as a parameter when you init the source.
In your newCustomCollectionSource.cs
private xxxViewController(your ViewController’s name) viewController;
public newCustomCollectionSource( xxxViewController viewController,…(other parameters) )
{
. . .
this. viewController= viewController;
. . .
}
And set the value of the member when you select the item as same as above :
public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath)
{
…
this.viewController.myIndexPath= NSIndexPath.FromRowSection(indexPath.Row, indexPath. Section);
}
In your ViewController:
CustomColllectionView.Source = new
newCustomCollectionSource(this,imageLocations);
You can get the index of the selected cell just like myIndexPath.Row, myIndexPath. Section in your ViewController.
I am collaborating with a designer who styles elements on my Xamarin project storyboard. I have trouble referencing his carefully placed elements in code. All the outlets are created, but in a UICollectionViewCell the UILabels etc are not instantiated. Here is code from a simple test app to demonstrate the problem.
The Xamarin generated code-behind:
[Register ("CardCell")]
partial class CardCell
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UILabel txtName { get; set; }
void ReleaseDesignerOutlets ()
{
if (txtName != null) {
txtName.Dispose ();
txtName = null;
}
}
}
My attempt to set the UI properties which causes the crash:
public partial class CardCell : UICollectionViewCell
{
public CardCell (IntPtr handle) : base (handle)
{
}
public void Update(string name)
{
txtName.Text = name; // throws exception here, because textName is null
}
}
The view controller with delegate methods:
public partial class TestCollectionController : UICollectionViewController
{
static NSString cardCellId = new NSString ("cardcell");
string[] cards = new string[] {"Red", "Green", "White", "Blue", "Pink", "Yellow"};
public TestCollectionController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
CollectionView.RegisterClassForCell (typeof(CardCell), cardCellId);
}
public override nint NumberOfSections (UICollectionView collectionView)
{
return 1;
}
public override nint GetItemsCount (UICollectionView collectionView, nint section)
{
return (nint)cards.Length;
}
public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
{
CardCell cell = (CardCell)collectionView.DequeueReusableCell (cardCellId, indexPath);
var card = cards [indexPath.Row];
cell.Update(card);
return cell;
}
}
I have tried both the Xamarin iOS Designer and Xcode's Interface Builder, but it does not make a difference. Any help will be greatly appreciated.
Examples of collection views driven by a storyboard are quite scarce, but I found this one and teased through it meticulously till I discovered that it did not include the call to register a class for the cell. So, remove this line:
CollectionView.RegisterClassForCell (typeof(CardCell), cardCellId);
and suddenly everything works as expected.
I was following this recipe and other examples, which all had the call included, which is only needed when you don't use a storyboard.
I'm attempting to provide a header and footer view from within an MvxCollectionViewController and I am having trouble. Normally, with a UICollectionViewController, I would override the GetViewForSupplementaryElement method like so:
public override UICollectionReusableView GetViewForSupplementaryElement (UICollectionView collectionView, NSString elementKind, NSIndexPath indexPath)
{
var someHeaderOrFooterView = (HeaderOrFooterView) collectionView.DequeueReusableSupplementaryView (elementKind, elementId, indexPath);
return someHeaderOrFooterView;
}
MvxCollectionViewControllers don't seem to get delegate callbacks to the GetViewForSupplementaryElement method like a UICollectionViewController does.
Is there another method for specifying the header and footer of a CollectionView using MvxCollectionViewController?
The standard steps should work (they work here...):
Provide a size for the header in the layout
HeaderReferenceSize = new System.Drawing.SizeF(100, 100),
Implement a class for the header
public class Header : UICollectionReusableView
{
UILabel label;
public string Text
{
get { return label.Text; }
set { label.Text = value; SetNeedsDisplay(); }
}
[Export("initWithFrame:")]
public Header(System.Drawing.RectangleF frame)
: base(frame)
{
label = new UILabel() { Frame = new System.Drawing.RectangleF(0, 0, 300, 50), BackgroundColor = UIColor.Yellow };
AddSubview(label);
}
}
register that class
CollectionView.RegisterClassForSupplementaryView(typeof(Header), UICollectionElementKindSection.Header, headerId);
Implement GetViewForSupplementaryElement
public override UICollectionReusableView GetViewForSupplementaryElement(UICollectionView collectionView, NSString elementKind, NSIndexPath indexPath)
{
var headerView = (Header)collectionView.DequeueReusableSupplementaryView(elementKind, headerId, indexPath);
headerView.Text = "Supplementary View";
return headerView;
}
Just tested these steps here and they work in my sample app (based on https://github.com/slodge/NPlus1DaysOfMvvmCross/tree/master/N-11-KittenView_Collections).
Aside> There is an open issue to provide bindable supplementary views - https://github.com/slodge/MvvmCross/issues/339 - but this issue shouldn't effect the basic collection view operation.
I was having the same problem when using MvvmCross, MvxCollectionViewController never call to
public override UICollectionReusableView
GetViewForSupplementaryElement
and I resolved the problem using the answer of "Perfem_element".
public class MyCollectionViewSource: MvxCollectionViewSource
{
public MyCollectionViewSource(UICollectionView collectionView, NSString defaultCellIdentifier) : base(collectionView, defaultCellIdentifier)
{
}
public override UICollectionReusableView GetViewForSupplementaryElement(UICollectionView collectionView, NSString elementKind, NSIndexPath indexPath)
{
var headerView = (MyHeader)collectionView.DequeueReusableSupplementaryView(elementKind, MyHeader.Key, indexPath);
headerView.Text = "Supplementary View";
return headerView;
}
and finally in MvxViewController:
var source = new MyCollectionViewSource (CollectionView, ListadoCollectionCell.Key);
Thanks
I was having the same problem with MvvmCross. I was able to solve it by subclassing the MvxCollectionViewSource class and overriding the GetViewForSupplementaryElement method:
public class MyCollectionViewSource: MvxCollectionViewSource
{
public TimelineSource(UICollectionView collectionView, NSString defaultCellIdentifier) : base(collectionView, defaultCellIdentifier)
{
}
public override UICollectionReusableView GetViewForSupplementaryElement(UICollectionView collectionView, NSString elementKind, NSIndexPath indexPath)
{
var headerView = (MyHeader)collectionView.DequeueReusableSupplementaryView(elementKind, MyHeader.Key, indexPath);
headerView.Text = "Supplementary View";
return headerView;
}
}
In my app i simply trying to add a checkmark to the selected row. I have around 20 items in my row and want to show a checkmark for the selected row. When i scroll to the bottom of the page and select a row it is throwing NullReferenceException. I have checkmark code reference from here
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.ObjCRuntime;
using System.Collections.Generic;
namespace SampleApp
{
public partial class FirstViewController : UITableViewController
{
DataSource dataSource;
public FirstViewController () : base ("FirstViewController", null)
{
Title = NSBundle.MainBundle.LocalizedString ("First", "First");
TabBarItem.Image = UIImage.FromBundle ("first");
}
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
// Add Table
TableView.Source = dataSource = new DataSource (this);
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
// Return true for supported orientations
return (toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown);
}
class DataSource : UITableViewSource
{
static readonly NSString CellIdentifier = new NSString ("DataSourceCell");
FirstViewController controller;
private List<String> _listData = new List<String> ();
private NSIndexPath _previousRow;
public DataSource (FirstViewController controller)
{
this.controller = controller;
// POPULATE DISPOSITION LIST
PopulateDatabase();
_previousRow = NSIndexPath.FromRowSection(Settings.SelectedIndex,0);
}
void PopulateDatabase()
{
_listData.Add("val1");
_listData.Add("val2");
_listData.Add("val3");
_listData.Add("val4");
_listData.Add("val5");
_listData.Add("val6");
_listData.Add("val7");
_listData.Add("val8");
_listData.Add("val9");
_listData.Add("val10");
_listData.Add("val11");
_listData.Add("val12");
_listData.Add("val13");
_listData.Add("val14");
_listData.Add("val15");
}
// Customize the number of sections in the table view.
public override int NumberOfSections (UITableView tableView)
{
return 1;
}
public override int RowsInSection (UITableView tableview, int section)
{
return _listData.Count;
}
// Customize the appearance of table view cells.
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell (CellIdentifier);
if (cell == null) {
cell = new UITableViewCell (UITableViewCellStyle.Default, CellIdentifier);
//cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
}
cell.TextLabel.Text = _listData[indexPath.Row];
cell.TextLabel.Font = UIFont.SystemFontOfSize(18);
return cell;
}
// Row Select
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
// Uncheck the previous row
if (_previousRow != null)
tableView.CellAt(_previousRow).Accessory = UITableViewCellAccessory.None;
// Do something with the row
var row = indexPath.Row;
Settings.SelectedIndex = row;
tableView.CellAt(indexPath).Accessory = UITableViewCellAccessory.Checkmark;
//Console.WriteLine("{0} selected",_controller.Items[row]);
_previousRow = indexPath;
// This is what the Settings does under Settings>Mail>Show on an iPhone
tableView.DeselectRow(indexPath,false);
}
// Set row height
public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
return 50f;
}
}
public class Settings
{
public static int SelectedIndex = 0;
}
}
}
I get the exception at line
tableView.CellAt(_previousRow).Accessory =UITableViewCellAccessory.None;
When i debug i found that Accessory
tableView.CellAt(_previousRow).Accessory Unknown member: Accessory
Any idea what is going wrong???
Got the anser
// Row Select
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
// Uncheck the previous row
if (_previousRow != null)
{
NSIndexPath [] temp = tableView.IndexPathsForVisibleRows;
if (temp.Contains(preIndex))
{
tableView.CellAt(_previousRow).Accessory = UITableViewCellAccessory.None;
}
}
// Do something with the row
var row = indexPath.Row;
Settings.SelectedIndex = row;
tableView.CellAt(indexPath).Accessory = UITableViewCellAccessory.Checkmark;
//Console.WriteLine("{0} selected",_controller.Items[row]);
_previousRow = indexPath;
// This is what the Settings does under Settings>Mail>Show on an iPhone
tableView.DeselectRow(indexPath,false);
}