Since this is my first blog post, if you find any spelling mistake then please forgive me.
As a Mobile App developer, I faced many issues in designing. Since in App developement using Xamarin.ios in .Net environment, i found many problems regarding some design features of UICollectionView, UITableView.
So I want to share a some simple code and tricks to design a good looking Left Aligned UICollectionView with Dynamic cell width. Here you can assign dynamic height also. But most of the Mobile App developer requires UICollectionView with dynamic cell width.
So here is the full code.
The whole code is written in C# and it will be more helpful for the Xamarin.ios developers.
1.First create a UICollectionView and add it in View where you want it to be displayed.
As a Mobile App developer, I faced many issues in designing. Since in App developement using Xamarin.ios in .Net environment, i found many problems regarding some design features of UICollectionView, UITableView.
So I want to share a some simple code and tricks to design a good looking Left Aligned UICollectionView with Dynamic cell width. Here you can assign dynamic height also. But most of the Mobile App developer requires UICollectionView with dynamic cell width.
So here is the full code.
The whole code is written in C# and it will be more helpful for the Xamarin.ios developers.
1.First create a UICollectionView and add it in View where you want it to be displayed.
UICollectionView collectionView;
2.Then create a UICollectionViewFlowLayout with any name. This FlowLayout will assign a frame to each of the cell that are created in UICollectionView.
UICollectionViewFlowLayout CollectionFlowLayout;
CollectionFlowLayout = new LeftAlignedCollectionViewFlowLayout();
if you are confused then let me explain why i had instantiated the flowlayout with custom name, because i had my whole code and method declaration in my custom flowlayout class named LeftAlignedCollectionViewFlowLayout.
3. Then instantiate a collectionView with some frame and pass it our custom flowLayout named CollectionFlowLayout
4. Write the class declaration and whole overridden methods in the LeftAlignedCollectionViewFlowLayout. The code is below.
class LeftAlignedCollectionViewFlowLayout : UICollectionViewFlowLayout
{
nfloat maxCellSpacing = 10;
public LeftAlignedCollectionViewFlowLayout()
{
}
public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(CGRect rect)
{
var arr = base.LayoutAttributesForElementsInRect(rect);
for (int i = 1; i < arr.Count(); ++i)
{
UICollectionViewLayoutAttributes currentLayoutAttributes = arr[i];
UICollectionViewLayoutAttributes prevLayoutAttributes = arr[i - 1];
nint maximumSpacing = 10;
nfloat origin = prevLayoutAttributes.Frame.GetMaxX();
if(origin + maximumSpacing+ currentLayoutAttributes.Frame.Size.Width<CollectionView.ContentSize.Width)
{
CGRect frame = currentLayoutAttributes.Frame;
frame.X = origin + maximumSpacing;
currentLayoutAttributes.Frame = frame;
}
}
return arr;
for (int i = 1; i < arr.Count(); ++i)
{
UICollectionViewLayoutAttributes currentLayoutAttributes = arr[i];
UICollectionViewLayoutAttributes prevLayoutAttributes = arr[i - 1];
nint maximumSpacing = 10;
nfloat origin = prevLayoutAttributes.Frame.GetMaxX();
if(origin + maximumSpacing+ currentLayoutAttributes.Frame.Size.Width<CollectionView.ContentSize.Width)
{
CGRect frame = currentLayoutAttributes.Frame;
frame.X = origin + maximumSpacing;
currentLayoutAttributes.Frame = frame;
}
}
return arr;
}
public override UICollectionViewLayoutAttributes LayoutAttributesForItem(NSIndexPath indexPath)
{
var currentItemAttributes = base.LayoutAttributesForItem(indexPath);
var collectionViewFlowLayout = CollectionView.CollectionViewLayout as UICollectionViewFlowLayout;
if (collectionViewFlowLayout != null)
{
var sectionInset = collectionViewFlowLayout.SectionInset;
if (indexPath.Item == 0)
{ // first item of section
var frame = currentItemAttributes.Frame;
frame.X = sectionInset.Left; // first item of the section should always be left aligned
currentItemAttributes.Frame = frame;
return currentItemAttributes;
}
var previousIndexPath = NSIndexPath.FromItemSection(indexPath.Item - 1, indexPath.Section);
var previousFrame = base.LayoutAttributesForItem(previousIndexPath).Frame;
previousFrame.X = base.LayoutAttributesForItem(previousIndexPath).Frame.Left;
if (previousFrame.X != base.LayoutAttributesForItem(previousIndexPath).Frame.Left)
{
var n = base.LayoutAttributesForItem(previousIndexPath).Frame.Left;
previousFrame.X = n;
maxCellSpacing = 0;
}
var previousFrameRightPoint = (previousFrame.X) + (previousFrame.Size.Width) + maxCellSpacing;
var currentFrame = currentItemAttributes.Frame;
var width = 0.0;
var collectionViewWidth = CollectionView == null ? 0 : CollectionView.Frame.Size.Width;
width = collectionViewWidth;
var strecthedCurrentFrame = new CGRect(0, currentFrame.Y, width, currentFrame.Size.Height);
if (CGRect.Intersect(previousFrame, strecthedCurrentFrame) == CGRect.Empty)
{ // if current item is the first item on the line
// the approach here is to take the current frame, left align it to the edge of the view
// then stretch it the width of the collection view, if it intersects with the previous frame then that means it
// is on the same line, otherwise it is on it's own new line
var frame = currentItemAttributes.Frame;
frame.X = sectionInset.Left; // first item on the line should always be left aligned
currentItemAttributes.Frame = frame;
return currentItemAttributes;
}
var frame2 = currentItemAttributes.Frame;
frame2.X = previousFrameRightPoint;
currentItemAttributes.Frame = frame2;
}
return currentItemAttributes;
}
}
5.Create and assign a data source to the our previously insaniated collectionView.
UIcollectionDInterestSource collectionDataSource;
collectionView.Source = collectionDataSource = new UIcollectionDInterestSource(this, collectionView, lstString);
Here 'this' parameter in the datasource is the instance of the class that holds our UICollectionView.
2nd parameter is the collectionView, and 3rd parameter is the actual data that is to be displayed in the cells of collection View.
My lstString is as follows.
List<string> lstString = new List<string> { "A Thing", "Another", "BTopic gsrg Thing", "A Thing", "Another", "BTopic Thing", "AB", "Interest BTopic", "My Interest", "Other Toipc here", "Some Topic", "A Thing", "Another", "BTopic", "Interest", "My Interest", "Other Toipc here", "Some Topic", "My Interest", "Other Toipc here", "Some Topic", "Vivek Gupta", "My Other Friend" };
The list contains string element with dynamic number of text.
Then register a UICollectionView class for our custom UICollectionView cells.
6. Now create a custom UICollectionViewDelegateFlowLayout that will assign a dynamic width to our cell as per the string of text.
collectionView.Delegate = new CollectionViewFlowDelegate(lstString, CollectionFlowLayout);
and the whole code for UICollectionViewDelegateFlowLayout is below:
class CollectionViewFlowDelegate : UICollectionViewDelegateFlowLayout
{
List<string> lstStr;
public CollectionViewFlowDelegate(List<string> lstString, UICollectionViewFlowLayout CollectionFlowLayout)
{
lstStr = lstString;
}
public override CGSize GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
CGSize size = new NSString(lstStr.ElementAt(indexPath.Row).ToString()).GetSizeUsingAttributes(new UIStringAttributes(NSDictionary.FromObjectAndKey(AppFonts.TitleFontSemiBold(15), UIStringAttributeKey.Font)));
size.Width += 20;
size.Height += 20;
collectionView.SystemLayoutSizeFittingSize(size, 1.0f, 1.0f);
return size;
}
}
7: The datasource class :
public class UIcollectionDInterestSource : UICollectionViewSource
{
MyProfileController controller;
List<string> lstStr;
public UIcollectionDInterestSource(MyProfileController controller, UICollectionView collectionView, List<string> lstStr)
{
this.controller = controller;
this.lstStr = lstStr;
}
public override nint NumberOfSections(UICollectionView collectionView)
{
return 1;
}
public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
return lstStr.Count;
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = (MyInterestCollectionViewCell)collectionView.DequeueReusableCell(MyInterestCollectionViewCell.CellId, indexPath);
cell.UpdateCell(lstStr.ElementAt(indexPath.Row));
cell.ContentView.AddGestureRecognizer(new UILongPressGestureRecognizer(LongPress));
cell.ContentView.AddGestureRecognizer(new UITapGestureRecognizer(TapGesture));
cell.ContentView.Layer.BorderColor = UIColor.Clear.FromHexString(AppTheme.AttendeeNetworkColor, 1.0f).CGColor;
cell.ContentView.Layer.BorderWidth = 2.0f;
cell.ContentView.Layer.CornerRadius = 4.0f;
Console.WriteLine(cell.Frame);
return cell;
}
public override void ItemHighlighted(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.CellForItem(indexPath);
}
public override void ItemUnhighlighted(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.CellForItem(indexPath);
}
public override bool ShouldHighlightItem(UICollectionView collectionView, NSIndexPath indexPath)
{
return true;
}
public override bool ShouldSelectItem(UICollectionView collectionView, NSIndexPath indexPath)
{
return true;
}
// for edit menu
public override bool ShouldShowMenu(UICollectionView collectionView, NSIndexPath indexPath)
{
return false;
}
public override bool CanPerformAction(UICollectionView collectionView, Selector action, NSIndexPath indexPath, NSObject sender)
{
if (action == new Selector("custom"))
return true;
else
return false;
}
public override void PerformAction(UICollectionView collectionView, Selector action, NSIndexPath indexPath, NSObject sender)
{
System.Diagnostics.Debug.WriteLine("code to perform action");
}
}