UICollectionView Cells not displaying (MonoTouch/Xamarin) - ios

Working with a UICollectionView in Xamarin just as I have before but running into a strange problem -- When the view containing the collection loads, I see the cells for a short moment and then they disappear. I double checked the ContentSize of the CollectionView and apparently it's defaulting to 0 width, 0 height but setting it manually doesn't seem to solve the problem. The CollectionView seems to stick around (if I set the background color to black I see a black View in the parent) but the cells are disappearing
Parent View (UIView subclass):
UICollectionViewFlowLayout layout = new UICollectionViewFlowLayout ();
layout.ItemSize = new SizeF (274, 281);
layout.MinimumInteritemSpacing = 3;
layout.ScrollDirection = UICollectionViewScrollDirection.Horizontal;
var haulCollection = new HaulCollectionController(layout);
haulCollection.CollectionView.Frame = new RectangleF (0, cellHeader.Frame.Bottom, cellHeader.Frame.Width, 281);
AddSubview(haulCollection.CollectionView);
UICollectionViewController:
public class HaulCollectionController : UICollectionViewController
{
public HaulCollectionController (UICollectionViewLayout layout) : base (layout)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
CollectionView.BackgroundColor = UIColor.Clear;
CollectionView.RegisterClassForCell (typeof(HaulCollectionCell), HaulCollectionCell.Key);
}
public override int NumberOfSections (UICollectionView collectionView)
{
return 1;
}
public override int GetItemsCount (UICollectionView collectionView, int section)
{
return 6;
}
public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.DequeueReusableCell (HaulCollectionCell.Key, indexPath) as HaulCollectionCell;
return cell;
}
public override bool ShouldHighlightItem (UICollectionView collectionView, NSIndexPath indexPath)
{
return false;
}
}
UICollectionViewCell:
public class HaulCollectionCell : UICollectionViewCell
{
public static readonly NSString Key = new NSString ("HaulCollectionCell");
public UILabel Retailer { get; set; }
public UILabel Brand { get; set; }
public UILabel ItemName { get; set; }
[Export ("initWithFrame:")]
public HaulCollectionCell (RectangleF frame) : base (frame)
{
BackgroundColor = UIColor.Cyan;
var infoOverlay = new UIView (new RectangleF(0, Frame.Height-60, Frame.Width, 55)) {
BackgroundColor = UIColor.FromRGBA(255, 255, 255, 153)
};
Retailer = new UILabel (new RectangleF(15,10,100,22)) {
Font = ViewHelpers.GetFont(20, false),
Text = "DICK'S"
};
Brand = new UILabel (new RectangleF(Retailer.IntrinsicContentSize.Width + 10, 10, 100,22)) {
Font = ViewHelpers.GetFont(20, true),
Text = "Nike"
};
ItemName = new UILabel (new RectangleF(15, Brand.Frame.Bottom + 5, 200,30)) {
Font = ViewHelpers.GetFont(26, false),
Text = "Windrunner Tech Fleece"
};
infoOverlay.AddSubviews (Retailer,Brand,ItemName);
ContentView.Add (infoOverlay);
}
}
can't figure out exactly why I can't get the cells to display...

Related

View height constraints are not set correctly as I scroll UICollectionView up and down

I'm trying to decide view heights based on a model property, but as UICollectionView is scrolled up and down, incorrect heights are assigned to visible cells. It seems that setting a HeightAnchor in GetCell (i.e. cellForItemAtIndexPath) does not work. How can I make this work?
using CoreGraphics;
using Foundation;
using System;
using System.Collections.Generic;
using UIKit;
namespace App2
{
public partial class ViewController : UIViewController
{
private UICollectionView _collectionView;
public ViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
InitializeCollectionView();
}
private void InitializeCollectionView()
{
_collectionView = new UICollectionView(View.Frame, new UICollectionViewCompositionalLayout(GetSection()))
{
DataSource = new CustomUICollectionViewDataSource(),
TranslatesAutoresizingMaskIntoConstraints = false
};
_collectionView.RegisterClassForCell(typeof(CustomUICollectionViewCell), "CustomUICollectionViewCell");
View.AddSubview(_collectionView);
NSLayoutConstraint.ActivateConstraints(new[]
{
_collectionView.TopAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.TopAnchor),
_collectionView.BottomAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.BottomAnchor),
_collectionView.LeftAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.LeftAnchor),
_collectionView.RightAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.RightAnchor)
});
}
private static NSCollectionLayoutSection GetSection()
{
var size = NSCollectionLayoutSize.Create(NSCollectionLayoutDimension.CreateFractionalWidth(1), NSCollectionLayoutDimension.CreateEstimated(50));
var item = NSCollectionLayoutItem.Create(size);
var group = NSCollectionLayoutGroup.CreateHorizontal(layoutSize: size, subitem: item, count: 1);
var section = NSCollectionLayoutSection.Create(group);
section.InterGroupSpacing = 5;
return section;
}
}
public class CustomUICollectionViewDataSource : UICollectionViewDataSource
{
private readonly List<Model> _models = new List<Model>
{
new Model {Height = 250},
new Model {Height = 100},
new Model {Height = 300},
new Model {Height = 400},
new Model {Height = 500},
new Model {Height = 50},
new Model {Height = 230},
new Model {Height = 100},
new Model {Height = 600},
new Model {Height = 310},
new Model {Height = 150},
new Model {Height = 220}
};
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var model = _models[(int)indexPath.Item];
var cell = collectionView.DequeueReusableCell("CustomUICollectionViewCell", indexPath) as CustomUICollectionViewCell;
cell.UpdateHeight(model.Height);
return cell;
}
public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
return _models.Count;
}
}
public sealed class CustomUICollectionViewCell : UICollectionViewCell
{
private readonly UIView _uiView;
[Export("initWithFrame:")]
public CustomUICollectionViewCell(CGRect frame) : base(frame)
{
_uiView = new UIView
{
BackgroundColor = UIColor.Brown,
TranslatesAutoresizingMaskIntoConstraints = false
};
ContentView.AddSubview(_uiView);
NSLayoutConstraint.ActivateConstraints(new[]
{
_uiView.TopAnchor.ConstraintEqualTo(ContentView.SafeAreaLayoutGuide.TopAnchor),
_uiView.BottomAnchor.ConstraintEqualTo(ContentView.SafeAreaLayoutGuide.BottomAnchor),
_uiView.LeftAnchor.ConstraintEqualTo(ContentView.SafeAreaLayoutGuide.LeftAnchor),
_uiView.RightAnchor.ConstraintEqualTo(ContentView.SafeAreaLayoutGuide.RightAnchor)
});
}
public void UpdateHeight(int height)
{
_uiView.HeightAnchor.ConstraintEqualTo(height).Active = true;
}
}
public class Model
{
public int Height { get; set; }
}
}
If you do this, the print message should prompt you to repeat the constraint.
You set the constraints of left, right, bottom, top, and added a height constraint when updating. The first four constraints have already determined the final height, the new height here will not work, and a warning message will be printed.
If you really want to update the height, you should set the left, right, top, height constraints from the beginning, and save the height constraint, which is used when updating.
var heightConstraint: NSLayoutConstraint?
heightConstraint = _uiView.heightAnchor.constraint(equalToConstant: 50)//Defaults
NSLayoutConstraint.activate([
(_uiView.topAnchor.constraint(equalTo:ContentView.SafeAreaLayoutGuide.topAnchor))!,
(_uiView.leftAnchor.constraint(equalTo:ContentView.SafeAreaLayoutGuide.leftAnchor))!
(_uiView.rightAnchor.constraint(equalTo:ContentView.SafeAreaLayoutGuide.rightAnchor))!,
(heightConstraint)!
]);
public void UpdateHeight(int height){
heightConstraint?.isActive = false
heightConstraint = _uiView.heightAnchor.constraint(equalToConstant: height)
heightConstraint?.isActive = true
}
Here's the fix for this as recommended by Xamarin support:
NSLayoutConstraint heightConstraint;
public void UpdateHeight(int height)
{
if (heightConstraint == null)
{
heightConstraint = _uiView.HeightAnchor.ConstraintEqualTo(height);
heightConstraint.Active = true;
}
else
{
heightConstraint.Constant = height;
}
}

Xamarin: UICollection Image reorder Issue

I am using UICollectionView to store images and I can reorder them by overriding CanMove And MoveItem.
But the items inside the UICollection only reorder when cell size is large like if cell size is around 106 height and width, then they can be reordered if they are smaller in size, they are cannot be reordered.
View:
public override void ViewDidLoad()
{
base.ViewDidLoad();
//ImageCv is the name of UiCollectionView
var collectionLayout = new PostImageFlowLayout(3, 0.85f);
var allCollectionSource = new PostImageColectionSource(ImageCv, (ViewModel as NewPostDetailViewModel));
ImageCv.RegisterNibForCell(PostImageCell.Nib, PostImageCell.Key);
ImageCv.RegisterClassForSupplementaryView(typeof(CollectionHeader), UICollectionElementKindSection.Header, new NSString("headerId"));
ImageCv.BackgroundColor = UIColor.Clear;
ImageCv.Hidden = false;
ImageCv.DataSource = allCollectionSource;
ImageCv.Delegate = collectionLayout;
var longPressGesture = new UILongPressGestureRecognizer(gesture =>
{
// Take action based on state
switch (gesture.State)
{
case UIGestureRecognizerState.Began:
var selectedIndexPath = ImageCv.IndexPathForItemAtPoint(gesture.LocationInView(View));
if (selectedIndexPath != null)
ImageCv.BeginInteractiveMovementForItem(selectedIndexPath);
Debug.WriteLine("Gesture Recognition: Activated");
break;
case UIGestureRecognizerState.Changed:
ImageCv.UpdateInteractiveMovement(gesture.LocationInView(View));
Debug.WriteLine("Gesture activated: Item location is changed");
break;
case UIGestureRecognizerState.Ended:
ImageCv.EndInteractiveMovement();
Debug.WriteLine("Gesture activation: complete");
break;
default:
ImageCv.CancelInteractiveMovement();
Debug.WriteLine("Gesture activation: Terminate");
break;
}
});
// Add the custom recognizer to the collection view
ImageCv.AddGestureRecognizer(longPressGesture);
}
UICollectionViewDelegateFlowLayout
using System;
using System.Windows.Input;
using CoreGraphics;
using UIKit;
namespace Sources.CollectionSources
{
public class PostImageFlowLayout : UICollectionViewDelegateFlowLayout
{
private float headerHeight;
private int noOfItems;
private bool isLoading;
public PostImageFlowLayout(int noOfItems, float headerHeight = 0f)
{
this.noOfItems = noOfItems;
this.headerHeight = headerHeight;
}
public override CGSize GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, Foundation.NSIndexPath indexPath)
{
return GetPostCellSize();
}
public override CGSize GetReferenceSizeForHeader(UICollectionView collectionView, UICollectionViewLayout layout, nint section)
{
return new CGSize(collectionView.Frame.Width, headerHeight);
}
public override UIEdgeInsets GetInsetForSection(UICollectionView collectionView, UICollectionViewLayout layout, nint section)
{
return new UIEdgeInsets(0, 0, 0, 0);
}
private CGSize GetPostCellSize()
{
var relativeWidth = (UIScreen.MainScreen.Bounds.Width - 2) / this.noOfItems;
return new CGSize(relativeWidth, relativeWidth);
//return new CGSize(55, 55);
}
}
}
Source
public class PostImageColectionSource : MvxCollectionViewSource
{
private NewPostDetailViewModel newPostDetailViewModel;
private string type;
static NSString animalCellId = new NSString("PostImageCell");
static NSString headerId = new NSString("Header");
List<IAnimal> animals;
protected override NSString DefaultCellIdentifier
{
get
{
return PostImageCell.Key;
}
}
public override System.Collections.IEnumerable ItemsSource
{
get
{
return base.ItemsSource;
}
set
{
base.ItemsSource = value;
CollectionView.ReloadData();
}
}
public PostImageColectionSource(UICollectionView collectionView, NewPostDetailViewModel newPostDetailViewModel) : base(collectionView)
{
this.newPostDetailViewModel = newPostDetailViewModel;
animals = new List<IAnimal>();
for (int i = 0; i < 20; i++)
{
animals.Add(new Monkey(i));
}
}
public override nint NumberOfSections(UICollectionView collectionView)
{
return 1;
}
public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
return 5;// animals.Count;
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = (PostImageCell)collectionView.DequeueReusableCell(animalCellId, indexPath);
var animal = animals[indexPath.Row];
cell.Result(indexPath.Row);
return cell;
}
public override bool CanMoveItem(UICollectionView collectionView, NSIndexPath indexPath)
{
Debug.WriteLine("Ready to move images");
//System.Diagnostics.Debug.WriteLine("Checking if it can move the item");
return true;
}
public override void MoveItem(UICollectionView collectionView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath)
{
//base.MoveItem(collectionView, sourceIndexPath, destinationIndexPath);
Debug.WriteLine("Start moving images to reorder");
var item = animals[(int)sourceIndexPath.Item];
animals.RemoveAt((int)sourceIndexPath.Item);
animals.Insert((int)destinationIndexPath.Item, item);
}
}
When the GetPostCellSize in PostImageFlowLayout has width and height of around 100, the CanMove and MoveItem in PostImageColectionSource are being called and items are being reordered. But if the GetPostCellSize has width and height of around 50 or 70, even though the gestures are activated, CanMove and MoveItem in PostImageColectionSource are not being called hence cannot be moved.
Can anyone hope me with reordering the images in UICollectionView when the cell size is small like around width and height of 70.
Thank you.
I am tagging swift and objective-C as this issue is related to IOS in general and not xamarin specific
Main issue here is that you need to pass in the collection view to the gesture.LocationInView(View) call instead of the main View. In ViewDidLoad in the UILongPressGestureRecognizer change:
var selectedIndexPath = ImageCv.IndexPathForItemAtPoint(gesture.LocationInView(View));
and
ImageCv.UpdateInteractiveMovement(gesture.LocationInView(View));
to
var selectedIndexPath = ImageCv.IndexPathForItemAtPoint(gesture.LocationInView(ImageCv)); // <-- pass in ImageCV instead of View. (where ImageCV is the collection view)
and
ImageCv.UpdateInteractiveMovement(gesture.LocationInView(ImageCv)); // <-- pass in ImageCV instead of View.
Another thing to note, but not a huge deal, is that PostImageColectionSource is ultimately derived from UICollectionViewSource, which is a combo of UICollectionViewDelegate and UICollectionViewDataSource in one class, but is being assigned to the DataSource property of the collection view. All this means is that though you can implement methods for UICollectionViewDelegate in PostImageColectionSource the delegate methods will not be called on that class since the Delegate property of the collection view is set to the PostImageFlowLayout, which derives ultimately from UICollectionViewDelegate via UICollectionViewDelegateFlowLayout.

Xamarin ios how to make a self-sizing UITableViewCell while editing

I am trying to build an self-sizing UITableView Cell. After googled, I found this tutorial: https://pontifex.azurewebsites.net/self-sizing-uitableviewcell-with-uitextview-in-ios-8/ which is quite good.
In swift, it's saying that tableView?.BeginUpdates can update the size of the custom cell. But It seems not working in xamarin ios.
Could someone help me on that? Many Thanks!
using System;
using Foundation;
using UIKit;
using CoreGraphics;
namespace Ma
{
public partial class DataInput : UITableViewCell
{
public string title { get; set;}
public static readonly UINib Nib = UINib.FromName ("DataInput", NSBundle.MainBundle);
public static readonly NSString Key = new NSString ("DataInput");
public string value { get; set;}
public DataInput (IntPtr handle) : base (handle)
{
}
public static DataInput Create ()
{
return (DataInput)Nib.Instantiate (null, null) [0];
}
public void Populate()
{
this.Title.Text = this.title;
if (!string.IsNullOrEmpty(value)) {
this.Input.Text = this.value;
}
}
public string GetInputValue()
{
return this.Input.Text;
}
public UITableView GetTableView()
{
UITableView table = null;
UIView view = this.Superview;
if (view != null) {
table = (UITableView)view.Superview;
}
return table;
}
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
this.Input.ScrollEnabled = false;
this.Input.Delegate = new DataInputDelegate ();
}
public override void SetSelected (bool selected, bool animated)
{
base.SetSelected (selected, animated);
if (selected) {
this.Input.BecomeFirstResponder ();
} else {
this.Input.ResignFirstResponder ();
}
}
}
public partial class DataInputDelegate : UITextViewDelegate
{
public override void Changed (UITextView textView)
{
var size = textView.Bounds.Size;
var newSize = textView.SizeThatFits (new CGSize (size.Width, size.Height));
if (size.Height != newSize.Height) {
UITextView.AnimationsEnabled = false;
UITableViewCell input = (UITableViewCell)textView.Superview.Superview;
UITableView tableView = (UITableView)input.Superview.Superview;
// This is the place of updating custom cell size, but It's not working now.
tableView.BeginUpdates ();
tableView.EndUpdates ();
UITextView.AnimationsEnabled = true;
var thisIndexPath = tableView.IndexPathForCell (input);
tableView.ScrollToRow (thisIndexPath, UITableViewScrollPosition.Bottom, false);
}
}
}
}
BTW, I am using autolayout and set
TableView.EstimatedRowHeight = 50;
TableView.RowHeight = UITableView.AutomaticDimension;
And I have done the following setting as well.
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
if (indexPath.Row == 0) {
return 80.0f;
}
return UITableView.AutomaticDimension;
}
Many thanks if someone can guide me!
Based on constraints placed on view, then autolayout will work. The code works fine after I set up the constraints of each components.

UITableView cuts my table view, cannot view the entire TableView[Xamarin, iOS]

I am trying to make a custom TableView that has big heights, but when i run it i can only access 2 of my 5 rows in the table(in the example i provided)
Here is a screen shot of how i am viewing my table : http://i.imgur.com/1dsPNj5.png
Here is the link to my Table Source : http://pastebin.com/B7U2BEd8
Here is my view controller :
unclass[] lol= new unclass[amount];
for (nint i = 0; i < amount; i++) {
lol [i] = new unclass ();
Console.WriteLine ("item created");
}
UITableView _table;
_table = new UITableView{ Frame = new CoreGraphics.CGRect (0, 30, View.Bounds.Width, 3000),Source= new TableSource(lol) };
_table.SeparatorStyle = UITableViewCellSeparatorStyle.None;
for (nint i = 0; i < amount; i++) {
lol [i].imager = await this.LoadImage (links[i]); //loads image from the net to table
}
View.AddSubview (_table);
}
I really don't understand why this is happening
Your TableSource is not the problem, I tested it with a blank table.
Also as Jason said you will need to change the table's frame height to "View.Bounds.Height - 30" -30 to compensate for your Y position. I created a simple example below that show all 5 cells. So it might be the way that you are adding the table or if there is anything else in the viewController. Are you able to post more of your view controller's code?
using UIKit;
using CoreGraphics;
using System;
using Foundation;
namespace SO_Xam_actvity
{
public class bigTableViewController : UIViewController
{
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
UITableView _table;
_table = new UITableView{ Frame = new CGRect (0, 30, View.Bounds.Width, View.Bounds.Height-30),Source= new TableSource(new [] {1,1,1,1,1}) };
_table.SeparatorStyle = UITableViewCellSeparatorStyle.None;
View.AddSubview (_table);
}
}
public class TableSource : UITableViewSource
{
int[] tableItems;
string cellIdentifier = bigTableViewCell.Key;
public TableSource (int[] items)
{
tableItems = items;
}
public override nint RowsInSection (UITableView tableview, nint section)
{
return tableItems.Length;
}
public override nfloat GetHeightForRow (UITableView tableView, NSIndexPath indexPath)
{
return 200;
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell (cellIdentifier) as bigTableViewCell;
if (cell == null) {
cell = new bigTableViewCell();
}
cell.DetailTextLabel.Text = $"{indexPath.Row}";
return cell;
}
}
public class bigTableViewCell : UITableViewCell
{
public static readonly NSString Key = new NSString ("bigTableViewCell");
public bigTableViewCell () : base (UITableViewCellStyle.Value1, Key)
{
TextLabel.Text = "TextLabel";
}
}
}

MvvmCross: How to create a UIWebView Element to auto resize its height based on the content?

How to create a custom UIWebView Element to auto resize its height based on the Html Content?
Here is my custom TableViewCell class and custom MonoTouch Dialog Element code.
I tried to obtain the height from the TableViewCell class and send it back to set the height for the Element class. However, I don't know how to do that.
public class WebContentViewCell : UITableViewCell
{
public static NSString Key = new NSString("WebContentViewCell");
public WebContentViewCell(string htmlContent, UITableView tv)
: base(UITableViewCellStyle.Default, Key)
{
View = new UIWebView();
View.LoadHtmlString(htmlContent, null);
ContentView.Add(View);
}
public void UpdateCell(string htmlContent)
{
View.LoadHtmlString(htmlContent, null);
}
public UIWebView View { get; set; }
public float Height { get; set; }
public override void LayoutSubviews()
{
base.LayoutSubviews();
var contentFrame = ContentView.Bounds;
View.Frame = contentFrame;
}
}
public class WebContentElement : Element, IElementSizing
{
private readonly string _htmlContent;
private float _height = 200;
private UIWebView _webView;
public WebContentElement(string htmlContent)
: base("")
{
_htmlContent = htmlContent;
_webView = new UIWebView();
_webView.LoadHtmlString(_htmlContent, null);
}
protected override UITableViewCell GetCellImpl(UITableView tv)
{
var cell = tv.DequeueReusableCell(WebContentViewCell.Key) ?? new WebContentViewCell(_htmlContent, tv);
return cell;
}
public float GetHeight(UITableView tableView, NSIndexPath indexPath)
{
return _height; // how to get the height based on the WebContentViewCell's content?
}
}

Resources