Working with Xamarin.ios and I need my UITableViewCell to adjust its height based on the size (number of lines of text) of a UILabel. I have a custom UITableViewCell where the UILabel is created and the text is set based on user input. The only way I really know how to adjust the height of the cell is in the UITableViewSource class using the public override nint RowsInSection(UITableView tableview, nint section) method.
public class ChatTableSource : UITableViewSource
{
public List<MessageDetails> messages;
public ChatTableSource()
{
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
CustomMessageCell cell = tableView.DequeueReusableCell(cellIdentifier) as CustomMessageCell;
// if there are no cells to reuse, create a new one
if (cell == null)
cell = new CustomMessageCell(cellIdentifier);
tableView.Layer.BackgroundColor = UIColor.White.CGColor;
cell.UpdateCell(messages[indexPath.Row].body);
return cell;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return messages.ToArray().Length;
}
//public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
//{
// return dynamic size
//}
}
public class CustomMessageCell : UITableViewCell
{
UILabel body;
public CustomMessageCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
body = new UILabel();
ContentView.Add(body);
}
public void UpdateCell(string body)
{
this.body.Text = body;
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
body.Frame = new CGRect(distanceFromScreenEdge, ContentView.Bounds.Top, longTextSize, ContentView.Bounds.Height);
body.Lines = 0;
}
}
I don't work with Xamarin - so I can't test this - but this should be what you want to do...
Use constraints and let auto-layout handle the sizing for you.
public class CustomMessageCell : UITableViewCell
{
UILabel body;
public CustomMessageCell(NSString cellId) : base(UITableViewCellStyle.Default, cellId)
{
body = new UILabel();
ContentView.Add(body);
var margins = View.LayoutMarginsGuide;
// Pin the leading edge of body to the margin
body.LeadingAnchor.ConstraintEqualTo (margins.LeadingAnchor).Active = true;
// Pin the trailing edge of body to the margin
body.TrailingAnchor.ConstraintEqualTo (margins.TrailingAnchor).Active = true;
// Pin the top edge of body to the margin
body.TopAnchor.ConstraintEqualTo (margins.TopAnchor).Active = true;
// Pin the bottom edge of body to the margin
body.BottomAnchor.ConstraintEqualTo (margins.BottomAnchor).Active = true;
// set body number of lines to Zero so it will automatically set its own height
body.Lines = 0
}
public void UpdateCell(string body)
{
this.body.Text = body;
}
}
You don't need LayoutSubviews() at all.
Related
I am new to iOS. I am creating TableView with swipe to delete the cell. But When I swipe every time the cell height is decrease. I am using iOS 10. The below code I have used.
Code :
class AppointmentSourceClass : UITableViewSource
{
List<AppointmentItem> appointments;
public AppointmentSourceClass(List<AppointmentItem> appointments)
{
this.appointments = appointments;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
ApointListCell cell = tableView.DequeueReusableCell(ApointListCell.Key) as ApointListCell ?? ApointListCell.Create();
var item = appointments[indexPath.Row];
cell.BindData(item);
cell.Layer.MasksToBounds = false;
cell.Layer.CornerRadius = 5.0f;
cell.BackgroundColor = UIColor.White;
cell.SelectionStyle = UITableViewCellSelectionStyle.None;
return cell;
}
public override void CommitEditingStyle(UITableView tableView, UITableViewCellEditingStyle editingStyle, NSIndexPath indexPath)
{
switch (editingStyle)
{
case UITableViewCellEditingStyle.Delete:
appointments.RemoveAt(indexPath.Row);
tableView.DeleteRows(new NSIndexPath[] { indexPath},UITableViewRowAnimation.Fade);
break;
case UITableViewCellEditingStyle.None:
break;
}
}
public override bool CanEditRow(UITableView tableView, NSIndexPath indexPath)
{
return true;
}
public override string TitleForDeleteConfirmation(UITableView tableView, NSIndexPath indexPath)
{
return "Trash ( ";
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return appointments.Count;
}
}
}
Output :
Before Swipe Image :
After Swipe Image :
Found the solution.
In the TableCell I have use below code to seperate the two tableCell and make space between them. If i remove the code then it working fine.
public override CoreGraphics.CGRect Frame
{
get
{
return base.Frame;
}
set
{
value.Y += 4;
value.Height -= 2 * 4;
base.Frame = value;
}
}
Good evening everybody.
Case Study:
Currently I'm working on an tvos application, that needs a Netflix like vertical scroll. I found the following article that explains the approach: https://www.thorntech.com/2015/08/want-your-swift-app-to-scroll-in-two-directions-like-netflix-heres-how/
Problem:
My constructor of the class ("DashboardCollectionView.cs") isn't called, so my cells aren't intialized. In result i receive a view without any collectionviewcells, like the photo below:
Information about code and the project:
I have attached the solution as a zip file. I hope someone can help me. I'm really new on .ios so maybe it can be something easy.
http://www35.zippyshare.com/v/cTJje8WL/file.html
EDIT:
Part of Code
public partial class DashboardCollectionView : UICollectionView
{
public DashboardCollectionView (IntPtr handle) : base (handle)
{
RegisterClassForCell(typeof(DashboardCollectionViewCell), "movieCell");
DataSource = new DashboardCollectionViewDataSource();
Delegate = new DashboardCollectionViewDelegateFlowLayout();
}
}
public partial class DashboardCollectionViewCell : UICollectionViewCell
{
public DashboardCollectionViewCell (IntPtr handle) : base (handle)
{
}
}
public class DashboardCollectionViewDataSource: UIKit.UICollectionViewDataSource
{
public DashboardCollectionViewDataSource()
{
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var movieCell = (DashboardCollectionViewCell)collectionView.DequeueReusableCell("movieCell", indexPath);
return movieCell;
}
public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
return 12;
}
public override nint NumberOfSections(UICollectionView collectionView)
{
return 1;
}
}
public class DashboardCollectionViewDelegateFlowLayout : UIKit.UICollectionViewDelegateFlowLayout
{
public DashboardCollectionViewDelegateFlowLayout()
{
}
public override CoreGraphics.CGSize GetSizeForItem(UIKit.UICollectionView collectionView, UIKit.UICollectionViewLayout layout, Foundation.NSIndexPath indexPath)
{
var itemsPerRow = 4;
var hardCodedPadding = 10;
var itemWidth = (collectionView.Bounds.Width / itemsPerRow) - hardCodedPadding;
var itemHeight = collectionView.Bounds.Height - (2 * hardCodedPadding);
return new CoreGraphics.CGSize(itemWidth, itemHeight);
}
}
public partial class DashboardTableViewController : UITableViewController
{
private String[] categories = new String[] { "Kürzlich hinzugefügt", "Beliebt" };
private String cardCellId = "cell";
public DashboardTableViewController(IntPtr handle) : base(handle)
{
}
public override nint NumberOfSections(UITableView tableView)
{
return categories.Length;
}
public override string TitleForHeader(UITableView tableView, nint section)
{
return categories[section];
}
public override nint RowsInSection(UITableView tableView, nint section)
{
return 1;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell(cardCellId, indexPath);
return cell;
}
}
The problem is this part
3. Now for the tricky part — wiring the Collection View’s DataSource and Delegate to the cell. It’s easier if you drag from the Collection View’s Connections Inspector to the Table View Cell within the view hierarchy.
I am not sure how to do that using VS story board, you probably need to use XCode. You kind of try to do it programmatically but that doesn't work.
So if working with XCode is not an option you can trick this part and give to your DashboardCollectionView a name to force its creation. But you cannot do that without creating custom class of your "Table View Cell".
You also need to comment out
//RegisterClassForCell(typeof(DashboardCollectionViewCell), "movieCell");
After those 3 steps you should be able to see your cells. I ran it on iOS 9 simulator but couldn't get any scrolling vertical or horizontal but this is a different question.
Here is a scenario that I am encountering. I am having a login screen with UserName as one of the fields. The user can select a user using a dropdown list (implemented via a button - to simulate drop down arrow, a popover controller and a table view). I having a controller (UserNameController) which has the logic of fetching the usernames and binding the same to the table view within it. The UserNameController is called via ViewController.cs which has the textfield and the dropdown button using below code:
var content = this.Storyboard.InstantiateViewController("UserNameLookUp") as UserNameController;
UIPopoverController popover = new UIPopoverController(content);
//popover.SetPopoverContentSize(new SizeF(80, 80), true);
popover.PresentFromRect(new RectangleF(float.Parse((sender.Frame.X + 115).ToString()),
float.Parse((sender.Frame.Y + 180).ToString())
, 80, 80), View, UIPopoverArrowDirection.Up, true);
and in UserNameController:
public override void ViewDidLoad()
{
string[] userName = new string[10];
tblVwUserName.Source = new TableSource(userName);
}
}
and TabelSource.cs looks like this:
public class TableSource : UITableViewSource
{
string[] TableItems;
string CellIdentifier = "TableCell";
public TableSource(string[] items)
{
TableItems = items;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return TableItems.Length;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell(CellIdentifier);
string item = TableItems[indexPath.Row];
//---- if there are no cells to reuse, create a new one
if (cell == null)
{ cell = new UITableViewCell(UITableViewCellStyle.Default, CellIdentifier); }
cell.TextLabel.Text = item;
return cell;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
// HOW TO SET BACK THE USERNAME TEXT FIELD IN VIEWCONTROLLER? AND CLOSE THE POPUP
tableView.DeselectRow(indexPath, true);
}
}
Now how do i need to display the selected username in the viewcontroller's textfield and close the popup?
Thanks!
Sid
I typically solve this requirement with properties on the TableViewSource and the TableViewController. Your source will change to:
public class TableSource : UITableViewSource
{
string[] TableItems;
string CellIdentifier = "TableCell";
public string SelectedItem {get; set;}
public TableSource(string[] items)
{
TableItems = items;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return TableItems.Length;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell(CellIdentifier);
string item = TableItems[indexPath.Row];
//---- if there are no cells to reuse, create a new one
if (cell == null)
{ cell = new UITableViewCell(UITableViewCellStyle.Default, CellIdentifier); }
cell.TextLabel.Text = item;
return cell;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
SelectedItem = items[indexPath.Row];
tableView.DeselectRow(indexPath, true);
}
}
Now you need a way to get that value. There are two possible ways:
1) UserNameController is of type UITableViewController, than it would be (content.TableView.Source as TableSource).SelectedItem
2) UserNameController is not of type UITableViewController, than I would add a property too that returns the property of the TableViewSource.
Last thing to do is closing the Popover and query the selected item. For this task I would wrap the UserNameController in a UINavigationController and add a Cancel and a Done button into it like this:
var navigationController = new UINavigationController(content);
var popover = new UIPopoverController(navigationController);
content.NavigationItem.SetLeftBarButtonItem(new UIBarButtonItem(UIBarButtonSystemItem.Cancel, (s, e) =>
{
parentController.DismissViewController(true, null);
}), true);
content.NavigationItem.SetRightBarButtonItem(new UIBarButtonItem(UIBarButtonSystemItem.Done, (s, e) =>
{
parentController.DismissViewController(true, null);
var selectedItem = (content.Source as TableSource).SelectedItem;
}), true);
I want to use UITableView on XamarinApp.
I tried UITableView example Populating a Table with Data ,but it doesn't work.
When i used this.Add(table); cause crash. When I remove this.Add(table) it's shows empty table.
Please help me...
Here is my code
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace KUkyuko
{
partial class MyTableViewSource : UITableView
{
public MyTableViewSource(IntPtr handle) : base (handle)
{
var table = this;
string[] tableItems = new string[] {"Vegetables","Fruits","Flower Buds","Legumes","Bulbs","Tubers"};
table.Source = new TableSource(tableItems);
this.Add(table); //this code cause crash
}
}
public class TableSource : UITableViewSource {
string[] TableItems;
string CellIdentifier = "TableCell";
public TableSource (string[] items)
{
TableItems = items;
}
public override nint RowsInSection (UITableView tableview, nint section)
{
return TableItems.Length;
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell (CellIdentifier);
string item = TableItems[indexPath.Row];
//---- if there are no cells to reuse, create a new one
if (cell == null)
{ cell = new UITableViewCell (UITableViewCellStyle.Default, CellIdentifier); }
cell.TextLabel.Text = item;
return cell;
}
}
}
Im abit unsure as to what you trying to achieve but
I would change the tableview to a tableViewController like so::
partial class TableViewController : UITableViewController
{
public TableViewController (IntPtr handle) : base (handle)
{
// Register the TableView's data source
string[] tableItems = new string[] {"Vegetables","Fruits","Flower Buds","Legumes","Bulbs","Tubers"};
this.TableView.Source = new TableSource(tableItems);
}
}
public class TableSource : UITableViewSource {
string[] TableItems;
string CellIdentifier = "TableCell";
public TableSource (string[] items)
{
TableItems = items;
}
public override nint RowsInSection (UITableView tableview, nint section)
{
return TableItems.Length;
}
public override nint NumberOfSections (UITableView tableView)
{
// TODO: return the actual number of sections
return 1;
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell (CellIdentifier);
string item = TableItems[indexPath.Row];
//---- if there are no cells to reuse, create a new one
if (cell == null)
{ cell = new UITableViewCell (UITableViewCellStyle.Default, CellIdentifier); }
cell.TextLabel.Text = item;
return cell;
}
}
If you are using the table inside a viewController then you need to do this part in your viewController and set up a outlet for the table.
The reason this code crashes as you are adding this to this:
var table = this;
string[] tableItems = new string[] {"Vegetables","Fruits","Flower Buds","Legumes","Bulbs","Tubers"};
table.Source = new TableSource(tableItems);
this.Add(table); //this code cause crash as it is the same as this.Add(this)
Hope this helps!
I am very new to XCode and Monotouch development. I am trying to add multiple tabs in root view (Master-Detail application). I am using Mono for development and xCode 4 for UI design. For example, one tab shows list of employees and another tab shows list of departments.
I removed the default table view in the rootviewController.xib file and replaced with tabbedview but I cant get new table view for each tab working.
I would appreciate if anyone can show me the correct way of doing this.
UPDATED: I use the code below to add 2 tabs and 2 table view. One is to hold "Securities" and the other one to hold the "Indicators"
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Collections.Generic;
namespace ChartQ
{
public partial class RootViewController : UITabBarController
{
public DetailViewController DetailVC { get; set; }
private List<SecurityInfo> listSecInfo = new List<SecurityInfo>();
private List<Indicator> listIndicator = new List<Indicator>();
public RootViewController () : base ("RootViewController", null)
{
this.Title = NSBundle.MainBundle.LocalizedString ("Securities", "Securities");
//this.ClearsSelectionOnViewWillAppear = false;
this.ContentSizeForViewInPopover = new SizeF (100f, 200f);
//this.ContentSizeForViewInPopover = new SizeF (320f, 600f);
// Custom initialization
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
//Read data from Database..
UITableViewController secController = new UITableViewController();
UITableViewController IndiController = new UITableViewController();
this.AddChildViewController(secController);
this.AddChildViewController(IndiController);
SQLLiteDatabase db = new SQLLiteDatabase();
listSecInfo = db.ReadSecurities();
listIndicator = db.ReadIndicator();
//TableView.Source = new SecuritiesDataSource (this);
secController.TableView.Source = new SecuritiesDataSource (this);
secController.TableView.SelectRow (NSIndexPath.FromRowSection (0, 0), false, UITableViewScrollPosition.Middle);
IndiController.TableView.Source = new IndicatorDataSource (this);
IndiController.TableView.SelectRow (NSIndexPath.FromRowSection (0, 0), false, UITableViewScrollPosition.Middle);
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
// Return true for supported orientations
return true;
}
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 ViewDidUnload ()
{
base.ViewDidUnload ();
// Clear any references to subviews of the main view in order to
// allow the Garbage Collector to collect them sooner.
//
// e.g. myOutlet.Dispose (); myOutlet = null;
ReleaseDesignerOutlets ();
}
class SecuritiesDataSource : UITableViewSource
{
static NSString cellIdentifier = new NSString ("CellId");
RootViewController controller;
public SecuritiesDataSource (RootViewController controller)
{
this.controller = controller;
}
// 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 controller.listSecInfo.Count;
}
public override string TitleForHeader (UITableView tableView, int section)
{
return "Securities";
}
// Customize the appearance of table view cells.
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
string cellIdentifier = "Cell";
var cell = tableView.DequeueReusableCell (cellIdentifier);
if (cell == null) {
cell = new UITableViewCell (UITableViewCellStyle.Subtitle, cellIdentifier);
//Add in a detail disclosure icon to each cell
cell.Accessory = UITableViewCellAccessory.DetailDisclosureButton;
}
// Configure the cell.
var sInfo = controller.listSecInfo [indexPath.Row];
cell.TextLabel.Text = String.Format ("{0}",sInfo.SecCode);
cell.DetailTextLabel.Text = sInfo.SecName;
return cell;
}
private void InfoAlert (string msg)
{
using (UIAlertView av = new UIAlertView("Info", msg, null, "OK", null)) {
av.Show ();
}
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
var sInfo = controller.listSecInfo [indexPath.Row];
controller.DetailVC.DrawChart(sInfo.SecID, sInfo.SecCode);
}
}
class IndicatorDataSource : UITableViewSource
{
static NSString cellIdentifier = new NSString ("CellId");
RootViewController controller;
public IndicatorDataSource (RootViewController controller)
{
this.controller = controller;
}
// 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 controller.listIndicator.Count;
}
public override string TitleForHeader (UITableView tableView, int section)
{
return "Indicators";
}
// Customize the appearance of table view cells.
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
string cellIdentifier = "Cell";
var cell = tableView.DequeueReusableCell (cellIdentifier);
if (cell == null) {
cell = new UITableViewCell (UITableViewCellStyle.Subtitle, cellIdentifier);
//Add in a detail disclosure icon to each cell
cell.Accessory = UITableViewCellAccessory.DetailDisclosureButton;
}
// Configure the cell.
var sInfo = controller.listIndicator [indexPath.Row];
cell.TextLabel.Text = String.Format ("{0}",sInfo.DescriptiveName);
cell.DetailTextLabel.Text = sInfo.ShortName;
return cell;
}
private void InfoAlert (string msg)
{
using (UIAlertView av = new UIAlertView("Info", msg, null, "OK", null)) {
av.Show ();
}
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
var sInfo = controller.listIndicator [indexPath.Row];
//controller.DetailVC.DrawChart(sInfo.SecID, sInfo.SecCode);
}
}
}
However, it crashes when I click the second tab. I am getting the error as below
"Got a SIGSEGV while executing native code"
The first list populated ok.
I think the UITableViewController objects are getting collected and causing your abort. Make secController and secController member variables.
public partial class RootViewController : UITabBarController
{
UITableViewController secController;
UITableViewController IndiController;
public DetailViewController DetailVC { get; set; }
...
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
//Read data from Database..
secController = new UITableViewController ();
IndiController = new UITableViewController ();