Custom EntryElement alignment changes on redraw / refresh - ios

I've created a custom EntryElement for our MonoTouch.Dialog so that all the controls are aligned to the right. The code is as follows:
public class RightAlignEntryElement : EntryElement
{
private NSString _cellKey = new NSString("RightAlignedEntryElement");
public RightAlignEntryElement(string caption, string placeholder, string value) : base(caption, placeholder, value)
{
}
protected override UITextField CreateTextField(RectangleF frame)
{
var paddedFrame = frame;
paddedFrame.Width = frame.Width -= 10;
var textField = base.CreateTextField(paddedFrame);
textField.TextAlignment = UITextAlignment.Right;
return textField;
}
public override UITableViewCell GetCell(UITableView tv)
{
var cell = base.GetCell(tv);
return cell;
}
protected override NSString CellKey
{
get { return _cellKey; }
}
}
this works fine, until the Dialog gets refreshed, for example the table is scrolled, or subview is presented then dismissed.
Before there is padding on the right of the cell, and once scrolled the padding disappears. I've seen that this has been a problem is the past, and have seen various other suggestions on here and the Xamarin forums, but nothing seems to work for me.
See the attached screen grabs for before and after.

For the record, here is the response I got on another forum for this question:
The behaviour is due to not using the cell reuse in you RightAlignEntryElement
Your GetCell method should look something like:
public override UITableViewCell GetCell(UITableView tv)
{
var cell = tv.DequeueReusableCell (CellKey) as UITableViewCell;
if (cell == null) {
cell = base.GetCell(tv);
cell.TextLabel.Font = UIFont.FromName("HelveticaNeue-Light", 16);
}
return cell;
}

Related

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.

GetViewForSupplementaryElement not getting called when trying to show heading in UICollectionView

I am trying to setup a UICollectionView within my existing UIViewController. Everything is working except for getting a title to show for each section - I can't figure out what I'm doing wrong.
My code in the UIViewController to initiate the collection view:
public partial class ViewController : UIViewController
{
//...
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
CollectionView_Outlet.RegisterClassForCell(typeof(ModifierCell), ModifierCell.CellID);
CollectionView_Outlet.RegisterClassForSupplementaryView (typeof(Header), UICollectionElementKindSection.Header, Header.HeaderId);
CollectionView_Outlet.ShowsHorizontalScrollIndicator = false;
CollectionView_Outlet.Source = new ModifiersSource(this);
CollectionView_Outlet.BackgroundColor = UIColor.White;
CollectionView_Outlet.ReloadData();
}
//...
}
Then I have created a subclass of UICollectionViewSource:
public class ModifiersSource : UICollectionViewSource
{
ViewController senderVC;
public ModifiersSource(ViewController sender)
{
senderVC = sender;
}
public override nint NumberOfSections(UICollectionView collectionView)
{
return 2;
}
public override nint GetItemsCount (UICollectionView collectionView, nint section)
{
return senderVC.modifiers.Count;
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
//...
}
public override UICollectionReusableView GetViewForSupplementaryElement(UICollectionView collectionView, NSString elementKind, NSIndexPath indexPath)
{
var headerView = (Header)collectionView.DequeueReusableSupplementaryView (elementKind, Header.HeaderId, indexPath);
headerView.Text = "Supplementary View";
return headerView;
}
}
And finally created:
public class Header : UICollectionReusableView
{
public static NSString HeaderId = new NSString("UserSource1");
UILabel label;
public string Text {
get {
return label.Text;
}
set {
label.Text = value;
SetNeedsDisplay ();
}
}
[Export ("initWithFrame:")]
public Header (RectangleF frame) : base (frame)
{
label = new UILabel (){
Frame = new RectangleF(0,0,300,50),
BackgroundColor = UIColor.Red};
AddSubview (label);
BackgroundColor = UIColor.White;
}
}
I've put a breakpoint on the GetViewForSupplementaryElement method but it never gets called. I've also set the following in my StoryBoard:
What am I missing?!
After many attempts of not being able to get the above to work, I manually set UICollectionViewFlowLayout whilst initiating the UIContainerView. Seems to have done the trick, but not sure why it didn't pick up the settings from my StoryBoard. Here is my working code:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
CollectionView_Outlet.RegisterClassForCell(typeof(ModifierCell), ModifierCell.CellID);
CollectionView_Outlet.RegisterClassForCell(typeof(ItemOptionCell), ItemOptionCell.CellID);
CollectionView_Outlet.RegisterClassForSupplementaryView (typeof(Header), UICollectionElementKindSection.Header, Header.HeaderId);
CollectionView_Outlet.ShowsHorizontalScrollIndicator = false;
//This is the new bit I added:
var layout = new UICollectionViewFlowLayout ();
layout.HeaderReferenceSize = new CGSize (300, 40);
CollectionView_Outlet.SetCollectionViewLayout (layout, false);
CollectionView_Outlet.Source = new ModifiersSource(this);
CollectionView_Outlet.ReloadData();
}
To get this to work in Xamarin, I had to enable Section Header under accessories (and it crashed in Xamarin, so from XCode). I had to do this even though I'm loading my header from a separate nib/xib into my collection view (but it will also show a reusable cell on the collection view that I don't think I need). Very specific but hopefully this saves someone in a similar situation some time!

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";
}
}
}

On a custom UITableViewCell with a button the text disappears sometimes - iOS 7

I've created a custom UITableViewCell with a UIButton in it. In iOS 6 it behaves as expected all the time. In iOS 7 it looks correct after the view is loaded the first time. But after any TableView.ReloadData() the text on the button disappears until I touch the button and swipe away to not trigger the click event.
You can see the described behavior in the movie: http://youtu.be/9SrKfouah7A
ButtonCellNew.cs
using System;
using MonoTouch.UIKit;
using System.Drawing;
namespace B2.Device.iOS
{
public class ButtonCellNew : UITableViewCell
{
private string _reuseIdentifier;
private bool _eventRegistered;
public Action<object, EventArgs> ButtonClickedAction;
public UIButton Button { get; set; }
public ButtonCellNew(string reuseIdentifier) : base()
{
_reuseIdentifier = reuseIdentifier;
Initialize();
}
public override string ReuseIdentifier
{
get
{
return _reuseIdentifier;
}
}
private void Initialize()
{
// Cell
SelectionStyle = UITableViewCellSelectionStyle.None;
// Button
Button = new UIButton(UIButtonType.Custom);
Button.Frame = Bounds;
Button.Font = UIFont.BoldSystemFontOfSize(15);
Button.SetTitleColor(Colors.ButtonTitle, UIControlState.Normal);
Button.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
ContentView.AddSubview(Button);
}
public override void PrepareForReuse()
{
base.PrepareForReuse();
Button.TitleLabel.Text = string.Empty;
}
public void RegisterEvents()
{
ButtonClickedAction = null;
if (!_eventRegistered)
{
Button.TouchUpInside += ButtonClicked;
_eventRegistered = true;
}
}
private void ButtonClicked(object sender, EventArgs e)
{
if (ButtonClickedAction != null)
ButtonClickedAction(sender, e);
}
}
}
MyTableViewController.cs
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
else if (indexPath.Section == (int)SupportTableViewSection.Button && indexPath.Row == (int)SupportTableViewButtonRow.Close)
{
var cell = tableView.DequeueReusableCell("buttonCell") as ButtonCellNew;
if (cell == null)
cell = new ButtonCellNew("buttonCell");
cell.Button.SetTitle("Support Einstellungen schliessen", UIControlState.Normal);
cell.RegisterEvents();
cell.ButtonClickedAction = ExitSupportSettings;
return cell;
}
}
Try removing the PrepareForReuse method in your ButtonCellNew class.
I had similar issues with it on iOS 7 and I removed it completely. It seems as its behavior has somewhat changed, as it's called by the system right before the cell is returned from the DequeueReusableCell method. Apparently, the "new" behavior includes calling the method at some other point also. Or... it's a bug or something.
It's not that important anyway, as Apple only suggests using it for resetting UI-related properties and not content-related properties.
So you can live without:
Button.TitleLabel.Text = string.Empty;

iOS custom collection view cell in Xamarin

I am trying to use Xamarin.iOS. Everything makes sense until I get to customizing UI. How would you create a collection with customized look and feel for the collection control and it's cell controls?
UICollectionView is a bit similar to UITableView. To get the customization you want, you first need to define your custom cell class:
public class AnimalCell : UICollectionViewCell
{
UIImageView imageView;
[Export ("initWithFrame:")]
public AnimalCell (System.Drawing.RectangleF frame) : base (frame)
{
BackgroundView = new UIView{BackgroundColor = UIColor.Orange};
SelectedBackgroundView = new UIView{BackgroundColor = UIColor.Green};
ContentView.Layer.BorderColor = UIColor.LightGray.CGColor;
ContentView.Layer.BorderWidth = 2.0f;
ContentView.BackgroundColor = UIColor.White;
ContentView.Transform = CGAffineTransform.MakeScale (0.8f, 0.8f);
imageView = new UIImageView (UIImage.FromBundle ("placeholder.png"));
imageView.Center = ContentView.Center;
imageView.Transform = CGAffineTransform.MakeScale (0.7f, 0.7f);
ContentView.AddSubview (imageView);
}
public UIImage Image {
set {
imageView.Image = value;
}
}
}
Then you can reference this cell in your UICollectionViewDataSource class by overriding the GetCell method:
public override UICollectionViewCell GetCell (UICollectionView collectionView, MonoTouch.Foundation.NSIndexPath indexPath)
{
var animalCell = (AnimalCell)collectionView.DequeueReusableCell (animalCellId, indexPath);
var animal = animals [indexPath.Row];
animalCell.Image = animal.Image;
return animalCell;
}
For more info, you should checkout this tutorial on Xamarin's website that I pulled these examples from:
http://docs.xamarin.com/guides/ios/user_interface/introduction_to_collection_views
to add a background image, try this (in ViewDidLoad)
myview.BackgroundColor = UIColor.FromPatternImage(UIImage.FromFile("myimage.png"));

Resources