I'm using the Monotouch.Dialog framework to build forms for user input my app. I have run into an odd problem where the screen displays overlapping text when I subclass UITableViewCell. When I say text overlapping I mean it looks as if one entire cell is placed on top of another, not just labels being pushed together or misplaced.
I followed this example on the Xamarin website here.
Basically I have this UITableViewCell class
public class SAFutureAdCell : UITableViewCell
{
private UILabel issueAndSizeLabel, mailDateLabel, orderDateLabel, miscLabel, totalLabel;
private UIImage monthlyImage, ccoImage;
public SAFutureAdCell(IntPtr p) : base(p)
{
init ();
}
public SAFutureAdCell(string resuseIdentifier) : base(UITableViewCellStyle.Default, resuseIdentifier)
{
init ();
}
public SAFutureAdCell(NSString cellKey): base(UITableViewCellStyle.Default, cellKey)
{
init ();
}
private void init()
{
issueAndSizeLabel = new UILabel() {
TextColor = UIColor.Black,
Font = UIFont.BoldSystemFontOfSize(15),
BackgroundColor = UIColor.Clear,
ShadowColor = UIColor.Clear,
TextAlignment = UITextAlignment.Left,
ClipsToBounds = true
};
mailDateLabel = new UILabel() {
TextColor = UIColor.Black,
Font = UIFont.BoldSystemFontOfSize(15),
BackgroundColor = UIColor.Clear,
ShadowColor = UIColor.Clear,
TextAlignment = UITextAlignment.Left,
ClipsToBounds = true
};
orderDateLabel = new UILabel() {
TextColor = UIColor.Gray,
Font = UIFont.SystemFontOfSize(13),
BackgroundColor = UIColor.Clear,
ShadowColor = UIColor.Clear,
TextAlignment = UITextAlignment.Left,
ClipsToBounds = true
};
miscLabel = new UILabel() {
TextColor = UIColor.Gray,
Font = UIFont.SystemFontOfSize(13),
BackgroundColor = UIColor.Clear,
ShadowColor = UIColor.Clear,
TextAlignment = UITextAlignment.Right,
ClipsToBounds = true
};
totalLabel = new UILabel() {
TextColor = UIColor.Black,
Font = UIFont.BoldSystemFontOfSize(15),
BackgroundColor = UIColor.Clear,
ShadowColor = UIColor.Clear,
TextAlignment = UITextAlignment.Right,
ClipsToBounds = true
};
monthlyImage = AppearanceManager.MonthlyIndicator; // small blue circle
ccoImage = AppearanceManager.CcoIndicator; // small red circle
ImageView.Image = monthlyImage;
ImageView.ContentMode = UIViewContentMode.ScaleAspectFit;
ContentView.AddSubviews(issueAndSizeLabel, mailDateLabel, orderDateLabel, miscLabel, totalLabel);
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
Accessory = UITableViewCellAccessory.DisclosureIndicator;
var b = ContentView.Bounds;
b.Width -= 30;
var x = 24;
issueAndSizeLabel.Frame = new RectangleF(x, 5, b.Width / 2, b.Height / 2 - 5).RoundFloats();
mailDateLabel.Frame = new RectangleF(issueAndSizeLabel.Frame.Right, 5, b.Width / 4, b.Height / 2 - 5).RoundFloats();
totalLabel.Frame = new RectangleF(mailDateLabel.Frame.Right, 5, b.Width / 4, b.Height / 2 - 5).RoundFloats();
orderDateLabel.Frame = new RectangleF(x, b.Height / 2 + 5, b.Width / 3, b.Height / 2 - 10).RoundFloats();
miscLabel.Frame = new RectangleF(totalLabel.Frame.Left, b.Height / 2 + 5, b.Width / 4, b.Height / 2 - 10).RoundFloats();
ImageView.Frame = new RectangleF(5, 0, AppearanceManager.MonthlyIndicator.Size.Width, ContentView.Frame.Height);
}
public void Update(Ad ad)
{
issueAndSizeLabel.Text = string.Format("{0} - {1}", ad.IssueCode, ad.AdvertSize);
mailDateLabel.Text = string.Format("Mail: {0}", ad.MailDate.ToShortDateString());
orderDateLabel.Text = string.Format("Order: {0}", ad.OrderDate.ToShortDateString());
miscLabel.Text = string.Format("Misc: {0}", ad.Misc.ToCurrency());
totalLabel.Text = string.Format("Total: {0}", ad.Total.ToCurrency());
if (ad.IsCCOPackage)
ImageView.Image = ccoImage;
else
ImageView.Image = monthlyImage;
ImageView.Alpha = (ad.IsMonthlyBilling || ad.IsCCOPackage) ? 1f : 0f;
}
}
This cell gets used from this Element.
public class SAFutureAdDetailElement : Element, IElementSizing
{
public SAFutureAdDetailElement(Ad ad, NSAction tapped) : base("FutureAdDetail")
{
this.ad = ad;
Tapped += tapped;
}
public SAFutureAdDetailElement(Ad ad) : base("FutureAdDetail")
{
this.ad = ad;
}
private static NSString cellKey = new NSString("SAFutureAdDetailElement");
protected override NSString CellKey
{
get
{
return cellKey;
}
}
public override UITableViewCell GetCell(UITableView tv)
{
var cell = tv.DequeueReusableCell (cellKey) as SAFutureAdCell;
if (cell == null) {
cell = new SAFutureAdCell(cellKey);
}
cell.Update (ad);
return cell;
}
public override void Selected(DialogViewController dvc, UITableView tableView, NSIndexPath path)
{
if (Tapped != null)
Tapped ();
tableView.DeselectRow (path, true);
}
public float GetHeight(UITableView tableView, NSIndexPath indexPath)
{
return cellHeight;
}
private Ad ad;
public event NSAction Tapped;
private int cellHeight = 60;
}
A collection of these elements are added to an MTD Section like so
Section CreateOnPageSection()
{
var onPageSection = new Section ("Future On Page Ads");
onPageSection.AddAll (
from ad in orderInfo.AdsFuture
where !Settings.offPageAdSizes.Contains (ad.AdvertSizeID)
select new SAFutureAdDetailElement (
ad,
() => {
NavigationController.PushViewController (new FutureAdDetailController (customer.CustID, ad, this), true);
}
)
);
return onPageSection;
}
Which is then added to a RootElement as a part of a DialogViewController. The root element has 3 other sections added to it, all with custom elements that have their own custom UITableViewCells and they are all very similar. They basically differ in the placement of the labels and the data that goes into the labels.
The text doesn't overlap until scrolling occurs or until you push a view onto the NavigationController and then pop back to this view. It is often difficult to reproduce on newer iPads as well (this app is iPad only). This issue happens much more frequently on older iPad 2s. Sometimes i can get it to happen on my Air but it is much more difficult to make it happen on that machine.
Here is a screenshot of the overlapping text.
OK so I found answer to my own question. It looks like there is an issue with how LayoutSubviews works with the cell recycling. Basically what I did was I got rid of the custom cell and moved all of the display loginc into my GetCell method in the custom element. The element class looks like so.
The only thing I don't like about this is updating the cell values is kind of a pain since I have to grab the
UILabelfrom an array ofUIViews and cast them as such like this((UILabel)cell.ContentView.Subviews[1]), And good luck keeping track of whichUILabelis which since you now can't use a variable name to reference theUILabel. Yes I did try making the labels a private member of the element and tried updating them that way which didn't work.