UICollectionViewDelegateFlowLayout has a method called sizeForItem (GetSizeForItem in MonoTouch).
But I'm not providing the delegate explicitly—instead, I'm inheriting from UICollectionViewController.
It mixes data source ands delegate functionality but doesn't have this method to override.
I tried adding this to my controller:
[Export ("collectionView:layout:sizeForItemAtIndexPath:")]
public virtual SizeF GetSizeForItem (UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
return new SizeF (100, 100);
}
and it was never called.
How do I provide this method without resorting to separating delegate and data source?
You can't. In Obj-C the viewcontroller (or any class) object can adopt the delegate protocol. This is not posible in Monotouch. You gave to use a delegate instance. But this can be a private class
public class CustomCollectionViewController:UICollectionViewController
{
public CustomCollectionViewController():base()
{
this.CollectionView.Delegate = new CustomViewDelegate();
}
class CustomViewDelegate: UICollectionViewDelegateFlowLayout
{
public override System.Drawing.SizeF GetSizeForItem (UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
return new System.Drawing.SizeF (100, 100);
}
}
}
Edit:
Without the need of creating a delegate subclass, add this in your UICollectionviewSource
/** Other methods such as GetItemsCount(), GetCell()... goes here **/
[Export ("collectionView:layout:sizeForItemAtIndexPath:"), CompilerGenerated]
public virtual CGSize GetSizeForItem (UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
return new CGSize (width, height);
}
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;
}
}
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.).