I have an ImageViewController that presents an ImageZoomView (which currently doesn't zoom yet, but it will soon). When I tap on the image, the entire image will shift upwards slightly. This should not happen, the image should stay in it's place until a user swipes or pinches.
The ImageViewController lives inside a UIPageViewController that allows swiping between views. However, the only issue I have is with the ImageViewController (i.e. the VideoViewController and PdfViewController both work fine and do not shift upwards on a tap)
ImageZoomView is a UIScrollView with a UIImageView as it's child control.
ImageZoomView:
public class ImageZoomView : UIScrollView
{
private UIImage _image;
private UIImageView _imageView;
public ImageZoomView()
{
_imageView = new UIImageView
{
ContentMode = UIViewContentMode.ScaleAspectFill
};
AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
ClipsToBounds = true;
AddSubview(_imageView);
}
public int Index { get; set; }
public override void LayoutSubviews()
{
base.LayoutSubviews();
var imageWidth = Bounds.Width;
_imageView.Frame = new CGRect(
Bounds.Left, Bounds.Top,
imageWidth, imageWidth / _image.Size.Width * _image.Size.Height);
ContentSize = _imageView.Bounds.Size;
}
public void DisplayImage(UIImage image)
{
_image = image;
_imageView.Image = image;
}
}
ImageViewController:
public class ImageViewController : AssetViewController
{
private ImageZoomView _imageView;
public ImageViewController(int index, Media media) : base(index, media) { }
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
_imageView.Frame = View.Bounds;
}
public override void LoadView()
{
_imageView = new ImageZoomView()
{
Index = Index,
BackgroundColor = UIColor.White,
};
_imageView.DisplayImage((_media as LocalMedia<UIImage>).Resource);
View = _imageView;
}
}
AssetViewController:
public abstract class AssetViewController : UIPageViewController
{
protected Media _media;
public AssetViewController(int index, Media media)
{
_media = media;
Index = index;
View.BackgroundColor = Globals.ColorDark;
}
public int Index { get; set; }
public Media CurrentMedia { get { return _media; } }
}
Few things causing problems
All your controllers are derived from UIPageViewController. The content controllers should not be derived from UIPageViewController
In ImageViewController the code View = _imageView; substitutes main View of controller to ScrollView. I suggest to use AddSubview(_imageView); What I saw that this erroneous movement shifts not the scroll view but somehow view of the whole controller.
LayoutSubviews is called pretty often and calculates the same thing all the time as neither image size nor scroll bounds are changing.
Below suggested improvements.
ImageViewController is content controller inside PageViewController and ImageZoomView is the scroll view inside ImageViewController
PageViewController
public partial class PageViewController : UIPageViewController
{
public PageViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
List<ImageViewController> pages = new List<ImageViewController>();
UIStoryboard board = UIStoryboard.FromName("Main", null);
for (int i = 0; i < 3; i++)
{
ImageViewController ctrl = (ImageViewController)board.InstantiateViewController("imageControllerStoryboardID");
ctrl.Index = i;
pages.Add(ctrl);
}
DataSource = new PageDataSource(pages);
View.Frame = View.Bounds;
//Set the initial content (first page)
SetViewControllers(new UIViewController[] { pages[0] }, UIPageViewControllerNavigationDirection.Forward, false, s => { });
}
}
public class PageDataSource : UIPageViewControllerDataSource
{
List<ImageViewController> pages;
public PageDataSource(List<ImageViewController> pages)
{
this.pages = pages;
}
override public UIViewController GetPreviousViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
var currentPage = referenceViewController as ImageViewController;
if (currentPage.Index == 0)
{
return pages[pages.Count - 1];
}
else
{
return pages[currentPage.Index - 1];
}
}
override public UIViewController GetNextViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
var currentPage = referenceViewController as ImageViewController;
return pages[(currentPage.Index + 1) % pages.Count];
}
}
ImageViewController
public partial class ImageViewController : UIViewController
{
public int Index;
private ImageZoomView _imageView;
public ImageViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
_imageView = new ImageZoomView(UIImage.FromFile("MergedImage.png"), View.Frame)
{
Index = Index,
BackgroundColor = UIColor.Blue,
};
View.AddSubview(_imageView);
}
}
ImageZoomView
public class ImageZoomView : UIScrollView
{
private UIImage _image;
private UIImageView _imageView;
public ImageZoomView(UIImage image, CGRect frame):base()
{
_imageView = new UIImageView
{
ContentMode = UIViewContentMode.ScaleAspectFill,
};
_image = image;
_imageView.Image = image;
Frame=_imageView.Frame = frame;
AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
ClipsToBounds = true;
AddSubview(_imageView);
ContentSize = new CGSize(Frame.Width, Frame.Width / _image.Size.Width * _image.Size.Height);
_imageView.Center = new CGPoint(ContentSize.Width / 2, ContentSize.Height / 2);
}
public int Index { get; set; }
}
There are still some code left from original sample as unused Index field and private _image variables. May be useful in the future, so I kept them around
Related
I have UIcollectionView with 8 sections. Each section has a dedicated array as the data source. I want to add an item to each section on a button press. UICollectionView scroll direction set to Horizontal because I want the sections to be vertical direction.
Everything working fine in Xcode project(Xib and Objective-C) as this screenshot(Red colored items are the items added on button press).
When I do the same implementation in Xamarin ios, the layout become a mess after press the button as bellow.
My code as bellow
public partial class GridViewController : UIViewController
{
CVSource source;
int MAX_NUMBER_OF_ROWS = 4;
public GridViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
//this.collectionView.SetNeedsDisplay();
this.collectionView.RegisterClassForCell(typeof(ItemCell), ItemCell.CellId);
source = new CVSource();
this.collectionView.DataSource = source;
}
partial void AddButtonPressed(UIButton sender)
{
Console.WriteLine("Add button pressed");
MAX_NUMBER_OF_ROWS = MAX_NUMBER_OF_ROWS + 1;
source.AddItem();
collectionView.Layer.RemoveAllAnimations();
collectionView.ReloadData();
ViewDidLayoutSubviews();
}
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
collectionView.Frame = new CoreGraphics.CGRect(collectionView.Frame.Location.X, collectionView.Frame.Location.Y, collectionView.Frame.Size.Width, MAX_NUMBER_OF_ROWS * 40);
btnAdd.Frame = new CoreGraphics.CGRect(btnAdd.Frame.Location.X, collectionView.Frame.Location.Y + collectionView.Frame.Size.Height + 10, btnAdd.Frame.Size.Width, btnAdd.Frame.Size.Height);
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
DataSource class
class CVSource : UICollectionViewSource
{
List<string> arr1 = new List<string> { "40", "30"};
List<string> arr2 = new List<string> { "25", "22" };
List<string> arr3 = new List<string> { "35", "67" };
List<string> arr4 = new List<string> { "26", "12" };
List<string> arr5 = new List<string> { "27", "21", };
List<string> arr6 = new List<string> { "12", "45" };
List<string> arr7 = new List<string> { "34", "67" };
List<string> arr8 = new List<string> { "21", "44", };
public override nint NumberOfSections(UICollectionView collectionView)
{
return 8;
}
public override nint GetItemsCount(UICollectionView collectionView, nint section)
{
if ((int)section == 0)
return arr1.Count;
else if ((int)section == 1)
return arr2.Count;
else if ((int)section == 2)
return arr3.Count;
else if ((int)section == 3)
return arr4.Count;
else if ((int)section == 4)
return arr5.Count;
else if ((int)section == 5)
return arr6.Count;
else if ((int)section == 6)
return arr7.Count;
else if ((int)section == 7)
return arr8.Count;
return 0;
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, Foundation.NSIndexPath indexPath)
{
var itemCell = (ItemCell)collectionView.DequeueReusableCell(ItemCell.CellId, indexPath);
if (indexPath.Section == 0)
{
itemCell.SetText(arr1[indexPath.Row]);
}
else if (indexPath.Section == 1)
{
itemCell.SetText(arr2[indexPath.Row]);
}
else if (indexPath.Section == 2)
{
itemCell.SetText(arr3[indexPath.Row]);
}
else if (indexPath.Section == 3)
{
itemCell.SetText(arr4[indexPath.Row]);
}
else if (indexPath.Section == 4)
{
itemCell.SetText(arr5[indexPath.Row]);
}
else if (indexPath.Section == 5)
{
itemCell.SetText(arr6[indexPath.Row]);
}
else if (indexPath.Section == 6)
{
itemCell.SetText(arr7[indexPath.Row]);
}
else if (indexPath.Section == 7)
itemCell.SetText(arr8[(indexPath.Row)]);
return itemCell;
}
public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath)
{
Console.WriteLine("Row selected "+ indexPath.Row);
}
public void AddItem()
{
arr1.Add("0");
arr2.Add("0");
arr3.Add("0");
arr4.Add("0");
arr5.Add("0");
arr6.Add("0");
arr7.Add("0");
arr8.Add("0");
}
}
ItemCell class
public partial class ItemCell : UICollectionViewCell
{
public static readonly NSString CellId = new NSString("ItemCell");
public static UITextField txtFld;
[Export("initWithFrame:")]
public ItemCell(CGRect 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);
txtFld = new UITextField() { Frame = new CGRect(5.0, 5.0, 60.0, 30.0), KeyboardType = UIKeyboardType.DecimalPad };
}
}
Im not a Xamarin developer, appreciate any comments/suggestions from Xamarin experts
Maybe you miss UICollectionViewLayout to set for collectionView , you can set the GridLayout for collectionView to have a try .
this.collectionView.SetCollectionViewLayout(new GridLayout(), true); //add CollectionViewLayout
this.collectionView.RegisterClassForCell(typeof(ItemCell), ItemCell.CellId);
GridLayout inherits from UICollectionViewFlowLayout in Xamrarin iOS :
public class GridLayout : UICollectionViewFlowLayout
{
public GridLayout ()
{
}
public override bool ShouldInvalidateLayoutForBoundsChange (CGRect newBounds)
{
return true;
}
public override UICollectionViewLayoutAttributes LayoutAttributesForItem (NSIndexPath path)
{
return base.LayoutAttributesForItem (path);
}
public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect (CGRect rect)
{
return base.LayoutAttributesForElementsInRect (rect);
}
}
The effect :
Affter tried the shared code , the effect as follow :
The different with your codes only here :
public ViewController (IntPtr handle) : base (handle)
{
collectionView = new UICollectionView(new CGRect(0, 0, UIScreen.MainScreen.Bounds.Size.Width, 300), new GridLayout());
collectionView.RegisterClassForCell(typeof(ItemCell), ItemCell.CellId);
//collectionView.SetCollectionViewLayout(new GridLayout(), true);
collectionView.BackgroundColor = UIColor.Blue;
source = new CVSource();
collectionView.DataSource = source;
View.AddSubview(collectionView);
button = new UIButton(new CGRect(150, 400, 100, 50));
button.SetTitle("Hello", UIControlState.Normal);
button.BackgroundColor = UIColor.Red;
View.AddSubview(button);
button.TouchUpInside += Button_TouchUpInside;
}
Note : I use code to create Interface , and set GridLayout for CollectionView .Here is the sample link .
I have to override the height swipe delete button in Tableviewcell i used the below code it's work fine ios 10 but in ios11 i can't able find the UITableViewCellDeleteConfirmationView in layoutsubview class
foreach (var views in this.Subviews)
{
if (views.Class.Name.ToString() == new NSString("UITableViewCellDeleteConfirmationView"))
{
CGRect newFrame = views.Frame;
CGRect newframe1 = new CGRect(newFrame.X, 6, newFrame.Size.Width, 59);
views.Frame = newframe1;
foreach (var getButtonviews in views.Subviews)
{
Console.WriteLine("x:"+getButtonviews.Frame.X);
Console.WriteLine("W:"+getButtonviews.Frame.Width);
if (getButtonviews.Class.Name.ToString() == "_UITableViewCellActionButton")
{
UIImage image = UIImage.FromBundle("img_line");
UIButton button = (UIButton)getButtonviews;
UIImageView imageview = new UIImageView();
imageview.Frame = new CGRect(getButtonviews.Frame.X + 120, 0, 1, getButtonviews.Frame.Height);
imageview.Image = image;
button.AddSubview(imageview);
foreach (var getButton in getButtonviews.Subviews)
{
if (getButton.Class.Name.ToString() == "UIButtonLabel")
{
UILabel label = (UILabel)getButton;
label.Font = UIFont.FromName("ProximaNova-Regular", 13);
}
}
}
}
}
}
The view hierarchy inside tableview has been changed after iOS10.
iOS8 - iOS10
UITableView -> UITableViewCell -> UITableViewCellDeleteConfirmationView -> _UITableViewCellActionButton
iOS11
work with Xcode8
UITableView -> UITableViewWrapperView -> UISwipeActionPullView -> UISwipeActionStandardButton
work with Xcode9
UITableView -> UISwipeActionPullView -> UISwipeActionStandardButton
Solution:
I make the code work both at iOS8 - iOS11, and I put all the code at ViewWillLayoutSubviews in ViewController , but first , we need to know which cell we are selecting.
public class TableDelegate : UITableViewDelegate
{
YourViewController owner;
public TableDelegate(YourViewController vc){
owner = vc;
}
public override UITableViewRowAction[] EditActionsForRow(UITableView tableView, NSIndexPath indexPath)
{
UITableViewRowAction hiButton = UITableViewRowAction.Create(
UITableViewRowActionStyle.Default,
"Hi",
delegate {
Console.WriteLine("Hello World!");
});
return new UITableViewRowAction[] { hiButton };
}
public override void WillBeginEditing(UITableView tableView, NSIndexPath indexPath)
{
owner.selectIndexPath = indexPath;
owner.View.SetNeedsLayout();
}
public override void DidEndEditing(UITableView tableView, NSIndexPath indexPath)
{
owner.selectIndexPath = null;
}
}
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 partial class ViewController : UIViewController
{
protected ViewController(IntPtr handle) : base(handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public NSIndexPath selectIndexPath { get; set; }
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
string[] tableItems = new string[] { "Vegetables", "Fruits", "Flower Buds", "Legumes", "Bulbs", "Tubers" };
tableview.Source = new TableSource(tableItems);
tableview.Delegate = new TableDelegate(this);
}
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
if (this.selectIndexPath != null)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
// Code that uses features from iOS 11.0 and later
foreach (UIView subview in tableview.Subviews)
{
if (subview.Class.Name.ToString() == "UISwipeActionPullView")
{
foreach (var buttonViews in subview.Subviews)
{
if (buttonViews.Class.Name.ToString() == "UISwipeActionStandardButton")
{
//operate what you want.
}
}
}
}
}
else
{
// Code to support earlier iOS versions
UITableViewCell cell = tableview.CellAt(this.selectIndexPath);
foreach (UIView subview in cell.Subviews)
{
if (subview.Class.Name.ToString() == "UITableViewCellDeleteConfirmationView")
{
foreach (var buttonViews in subview.Subviews)
{
if (buttonViews.Class.Name.ToString() == "_UITableViewCellActionButton")
{
//operate what you want.
}
}
}
}
}
}
}
}
I have a strange behaviour on my Custom UICollectionView.
Everytime i call
KeyWordsFieldsCell _dummyCellForRendering = (KeyWordsFieldsCell)collectionView.DequeueReusableCell (KeyWordsFieldsCell.CellId, indexPath);
in
public override CGSize GetSizeForItem (UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
my Code crashes without Error or Stacktrace.
In
public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
however the call of
KeyWordsFieldsCell _dummyCellForRendering = (KeyWordsFieldsCell)collectionView.DequeueReusableCell (KeyWordsFieldsCell.CellId, indexPath);
just works fine.
Here is the complete Code of my UICollectionViews DataSource and Delegate.
namespace KeyWordFieldsView
{
#region CollectionViewDataSource
public class KeyWordsFieldDataSource : UICollectionViewDataSource
{
private readonly UICollectionView keyWordsCollectionView;
public KeyWordsFieldDataSource (UICollectionView keyWordsCollectionView)
{
this.keyWordsCollectionView = keyWordsCollectionView;
}
public event EventHandler ContentChangedEvent;
private List<String> data = new List<String> ();
public List<String> Data
{
get
{
return data;
}
set
{
data = value;
}
}
public override nint GetItemsCount (UICollectionView collectionView, nint section)
{
return data.Count;
}
public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
{
var textCell = (KeyWordsFieldsCell)collectionView.DequeueReusableCell (KeyWordsFieldsCell.CellId, indexPath);
textCell.initCell ();
textCell.Text = Data [indexPath.Row];
textCell.DeleteButtonPressedEvent += HandleDeleteButtonPressedEvent;
return textCell;
}
public void HandleDeleteButtonPressedEvent (object sender, EventArgs a)
{
if (sender.GetType () == typeof (KeyWordsFieldsCell))
{
var cell = sender as KeyWordsFieldsCell;
NSIndexPath [] pathsToDelete = { keyWordsCollectionView.IndexPathForCell (cell) };
if (pathsToDelete [0] != null)
{
cell.DeleteButtonPressedEvent -= HandleDeleteButtonPressedEvent;
Data.RemoveAt (pathsToDelete [0].Row);
keyWordsCollectionView.DeleteItems (pathsToDelete);
}
OnContentChanged (sender, a);
}
}
public void OnContentChanged (object sender, EventArgs ea)
{
if (ContentChangedEvent != null)
{
ContentChangedEvent (this, ea);
}
}
}
#endregion
#region CollectionViewDelegate
class KeyWordsFieldDelegate : UICollectionViewDelegateFlowLayout
{
public override CGSize GetSizeForItem (UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
List<String> data = ((KeyWordsFieldDataSource)collectionView.DataSource).Data;
KeyWordsFieldsCell _dummyCellForRendering = (KeyWordsFieldsCell)collectionView.DequeueReusableCell (KeyWordsFieldsCell.CellId, indexPath);
_dummyCellForRendering.Text = data [indexPath.Row];
_dummyCellForRendering.keyWordContainerView.SetNeedsLayout ();
_dummyCellForRendering.keyWordContainerView.LayoutIfNeeded ();
double height = Math.Max (_dummyCellForRendering.keyWordLabel.Frame.Height, _dummyCellForRendering.keyWordFieldDeleteButton.Frame.Height);
double width = Math.Min (_dummyCellForRendering.keyWordContainerView.Frame.Width, collectionView.Bounds.Width);
_dummyCellForRendering = null;
return new CGSize (width, height);;
}
public override void ItemSelected (UICollectionView collectionView, NSIndexPath indexPath)
{
}
public override bool ShouldSelectItem (UICollectionView collectionView, NSIndexPath indexPath)
{
return true;
}
public override void CellDisplayingEnded (UICollectionView collectionView, UICollectionViewCell cell, NSIndexPath indexPath)
{
var keyWordCell = cell as KeyWordsFieldsCell;
keyWordCell.DeleteButtonPressedEvent -= ((KeyWordsFieldDataSource)collectionView.DataSource).HandleDeleteButtonPressedEvent;
}
}
#endregion
#region left justified cells
class LeftAlignedCollectionViewFlowLayout : UICollectionViewFlowLayout
{
nfloat maxCellSpacing = 4;
public override UICollectionViewLayoutAttributes [] LayoutAttributesForElementsInRect (CGRect rect)
{
var attributesForElementsInRect = base.LayoutAttributesForElementsInRect (rect);
UICollectionViewLayoutAttributes [] newAttributesForElementsInRect = new UICollectionViewLayoutAttributes [attributesForElementsInRect.Count ()];
var leftMargin = this.SectionInset.Left;
for (int i = 0; i < attributesForElementsInRect.Count (); i++)
{
var attributes = attributesForElementsInRect [i];
//if Element is first in new Line and already leftaligned or if element is in new line
if (attributes.Frame.X == leftMargin || attributes.Frame.Y > attributesForElementsInRect[i > 0 ? i-1 : i].Frame.Y)
{
leftMargin = this.SectionInset.Left; //reset the leftMargin to left sectionInset.
}
CGRect newLeftAlignedFrame = attributes.Frame;
newLeftAlignedFrame.X = leftMargin;
attributes.Frame = newLeftAlignedFrame;
leftMargin += attributes.Size.Width + maxCellSpacing;
newAttributesForElementsInRect [i] = attributes;
}
return newAttributesForElementsInRect;
}
}
#endregion
}
and here is the code of my UICollectionViewCell
namespace KeyWordFieldsView
{
public partial class KeyWordsFieldsCell : UICollectionViewCell
{
protected KeyWordsFieldsCell (IntPtr handle) : base (handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public string Text
{
get
{
return keyWordLabel.Text;
}
set
{
initCell ();
keyWordLabel.Text = value;
keyWordLabel.SizeToFit ();
SetNeedsDisplay ();
}
}
public UILabel keyWordLabel;
public UIButton keyWordFieldDeleteButton;
public UIView keyWordContainerView;
public static readonly NSString CellId = new NSString ("KeyWordsFieldsCell");
public event EventHandler DeleteButtonPressedEvent;
public void initCell () {
UIColor chipGrey = UIColor.FromRGBA (153, 153, 153, 51);
ContentView.BackgroundColor = chipGrey;
ContentView.Layer.CornerRadius = 16;
if (keyWordContainerView == null)
{
keyWordContainerView = new UIView (new CGRect (0, 0, 0, 32));
keyWordContainerView.TranslatesAutoresizingMaskIntoConstraints = false;
keyWordContainerView.BackgroundColor = UIColor.Clear;
ContentView.AddSubview (keyWordContainerView);
}
if (keyWordLabel == null)
{
keyWordLabel = new UILabel (new CGRect (0, 0, 0, 32));
keyWordLabel.BackgroundColor = UIColor.Clear;
UIFont labelFont = UIFont.SystemFontOfSize (14f);
keyWordLabel.Font = labelFont;
keyWordLabel.TranslatesAutoresizingMaskIntoConstraints = false;
keyWordLabel.LineBreakMode = UILineBreakMode.MiddleTruncation;
keyWordContainerView.AddSubview (keyWordLabel);
}
if (keyWordFieldDeleteButton == null)
{
keyWordFieldDeleteButton = UIButton.FromType (UIButtonType.Custom);
keyWordFieldDeleteButton.Frame = new CGRect (0, 0, 32, 32);
keyWordFieldDeleteButton.SetImage (UIImage.FromBundle ("remove-icon"), UIControlState.Normal);
keyWordFieldDeleteButton.BackgroundColor = UIColor.Clear;
keyWordFieldDeleteButton.TouchUpInside += DeleteButtonPressed;
keyWordFieldDeleteButton.TranslatesAutoresizingMaskIntoConstraints = false;
keyWordContainerView.AddSubview (keyWordFieldDeleteButton);
}
else {
//Add ButtonEvent in Case of Reuse
keyWordFieldDeleteButton.TouchUpInside -= DeleteButtonPressed;
keyWordFieldDeleteButton.TouchUpInside += DeleteButtonPressed;
}
var cvDictionary = NSDictionary.FromObjectsAndKeys (new NSObject [] { keyWordContainerView }, new NSObject [] { new NSString ("kwcv") });
ContentView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("H:|[kwcv]|", 0, new NSDictionary (), cvDictionary));
ContentView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("V:|[kwcv]|", 0, new NSDictionary (), cvDictionary));
keyWordContainerView.SetContentHuggingPriority (249, UILayoutConstraintAxis.Vertical);
keyWordContainerView.SetContentCompressionResistancePriority (749, UILayoutConstraintAxis.Vertical);
var viewsDictionary = NSDictionary.FromObjectsAndKeys (new NSObject [] { keyWordLabel, keyWordFieldDeleteButton }, new NSObject [] { new NSString ("kwlbl"), new NSString ("kwbtn") });
keyWordContainerView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("H:|-[kwlbl][kwbtn(==32)]|", 0, new NSDictionary (), viewsDictionary));
keyWordContainerView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("V:|[kwbtn(==32)]|", 0, new NSDictionary (), viewsDictionary));
keyWordContainerView.AddConstraints (NSLayoutConstraint.FromVisualFormat ("V:|[kwlbl]|", 0, new NSDictionary (), viewsDictionary));
keyWordFieldDeleteButton.SetContentHuggingPriority (249, UILayoutConstraintAxis.Vertical);
keyWordFieldDeleteButton.SetContentCompressionResistancePriority (751, UILayoutConstraintAxis.Vertical);
keyWordLabel.SetContentHuggingPriority (249, UILayoutConstraintAxis.Vertical);
keyWordLabel.SetContentCompressionResistancePriority (749, UILayoutConstraintAxis.Vertical);
}
//[Export ("initWithFrame:")]
//public KeyWordsFieldsCell (CGRect frame) : base (frame)
//{
// initCell ();
//}
public override void LayoutSubviews ()
{
base.LayoutSubviews ();
}
public void DeleteButtonPressed (object sender, EventArgs ea)
{
((UIButton)sender).TouchUpInside -= DeleteButtonPressed;
OnDeleteButtonPressed (sender, ea);
}
void OnDeleteButtonPressed (object sender, EventArgs ea)
{
if (DeleteButtonPressedEvent != null)
{
DeleteButtonPressedEvent (this, ea);
}
}
}
}
This is where the UICollectionView gets initialized:
if (CollectionView != null && CollectionView.DataSource == null)
{
CollectionView.RegisterClassForCell (typeof (KeyWordsFieldsCell), KeyWordsFieldsCell.CellId);
CollectionView.TranslatesAutoresizingMaskIntoConstraints = false;
CollectionView.SetCollectionViewLayout (new LeftAlignedCollectionViewFlowLayout (), false);
KeyWordsFieldDataSource Source = new KeyWordsFieldDataSource (CollectionView);
if (data != null)
{
Source.Data = data;
}
CollectionView.DataSource = Source;
KeyWordsFieldDelegate keyWordsDelegate = new KeyWordsFieldDelegate ();
CollectionView.Delegate = keyWordsDelegate;
(CollectionView.CollectionViewLayout as UICollectionViewFlowLayout).MinimumLineSpacing = 4;
(CollectionView.CollectionViewLayout as UICollectionViewFlowLayout).MinimumInteritemSpacing = 2;
//CollectionViewHeightConstraint.Constant = CollectionView.CollectionViewLayout.CollectionViewContentSize.Height;
}
Hope that someone can help be, because this is a rather frustrating problem.
Greets,
Mav
So for anyone who stumbles across the same Problem.
Frankly speaking there is just a different behaviour between UITableView and UICollectionView.
Where in UITableView it is totally ok to call dequeueReusableCellWithReuseIdentifier() in getHeightForRow() to get a cell for height calculation, calling it in sizeForItemAtIndexPath in a UICollectionView will cause an indefinite loop and thus crash the app.
Thx to #Markus Rautopuro for point me in the right direction with his Answer
I'm now calculating the height of the cell, by calculating the size of the components in the cell. That works quite well and needs less resources, since i don't need to build a complete cell, but only the items, that add up to the height.
I'm having some trouble implementing a UICollectionView in my UIViewController. It gives me an error in GetCell Failed to marshal the Objective-C object 0x137622db0 (type: ParkXApp_iOS_FamilyCollectionCell). Could not find an existing managed instance for this object, nor was it possible to create a new managed instance (because the type 'ParkXApp_iOS.FamilyCollectionCell' does not have a constructor that takes one IntPtr argument)..
I have implemented next to the monotouch sample code: monotouch sample simple UICollectionView
this is my code:
using System;
using UIKit;
using CoreGraphics;
using System.Collections.Generic;
using ObjCRuntime;
using Foundation;
namespace ParkXApp_iOS
{
public class FamilyView : UIViewController
{
nfloat navBarHeight;
nfloat viewWidth;
nfloat viewHeight;
public FamilyView ()
{
}
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
Draw ();
}
public void Draw () {
// clean the screen before (re)drawing it
foreach (var subView in this.View.Subviews) {
subView.RemoveFromSuperview ();
}
this.NavigationItem.SetRightBarButtonItem(null, true);
// set background
this.View.BackgroundColor = new UIColor((nfloat)0.99, (nfloat)0.99, (nfloat)0.99, 1);
// get viewWidth and viewHeight for drawing relative to the screen
navBarHeight = this.NavigationController.NavigationBar.Frame.GetMaxY ();
viewWidth = UIScreen.MainScreen.Bounds.Width;
viewHeight = UIScreen.MainScreen.Bounds.Height - navBarHeight;
UICollectionViewFlowLayout flowLayout = new UICollectionViewFlowLayout ();
flowLayout.ItemSize = new CGSize (100, 100);
flowLayout.ScrollDirection = UICollectionViewScrollDirection.Vertical;
flowLayout.SectionInset = new UIEdgeInsets (20, 20, 20, 20);
UICollectionView collectionView = new FamilyCollectionView (flowLayout).CollectionView;
collectionView.Frame = new CGRect (0, navBarHeight, viewWidth, viewHeight);
collectionView.ContentInset = new UIEdgeInsets (50, 0, 0, 0);
this.View.AddSubview (collectionView);
}
}
public class FamilyCollectionView : UICollectionViewController
{
List<string> familyMembers;
public FamilyCollectionView (UICollectionViewLayout layout) : base (layout) {
familyMembers = new List<string> ();
for (int i = 0; i < 5; i++) {
familyMembers.Add ("tom");
}
CollectionView.RegisterClassForCell (typeof(FamilyCollectionCell), "FamilyCollectionCell");
UIMenuController.SharedMenuController.MenuItems = new UIMenuItem[] {
new UIMenuItem ("Custom", new Selector ("custom"))
};
}
public override nint NumberOfSections (UICollectionView collectionView)
{
return 1;
}
public override nint GetItemsCount (UICollectionView collectionView, nint section)
{
return familyMembers.Count;
}
public override UICollectionViewCell GetCell (UICollectionView collectionView, Foundation.NSIndexPath indexPath)
{
try {
var cell = (FamilyCollectionCell)collectionView.DequeueReusableCell ("FamilyCollectionCell", indexPath);
cell.nameLabel.Text = familyMembers [indexPath.Row];
return cell;
} catch (Exception ex) {
Console.WriteLine (ex.Message);
}
return null;
}
public override void ItemHighlighted (UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.CellForItem (indexPath);
cell.ContentView.BackgroundColor = UIColor.Yellow;
}
public override void ItemUnhighlighted (UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.CellForItem (indexPath);
cell.ContentView.BackgroundColor = UIColor.White;
}
public override bool ShouldHighlightItem (UICollectionView collectionView, NSIndexPath indexPath)
{
return true;
}
}
public class FamilyCollectionCell : UICollectionViewCell
{
public UILabel nameLabel;
public FamilyCollectionCell (CGRect frame) : base (frame)
{
BackgroundColor = UIColor.Orange;
ContentView.Layer.BorderColor = UIColor.LightGray.CGColor;
ContentView.Layer.BorderWidth = 2.0f;
ContentView.BackgroundColor = UIColor.White;
ContentView.Transform = CGAffineTransform.MakeScale (0.8f, 0.8f);
nameLabel = new UILabel ();
nameLabel.Text = "name";
nameLabel.SizeToFit ();
nameLabel.Center = ContentView.Center;
ContentView.AddSubview (nameLabel);
}
[Export("custom")]
void Custom()
{
Console.WriteLine ("some code in the cell");
}
public override bool CanPerform (Selector action, NSObject withSender)
{
if (action == new Selector ("custom"))
return true;
else
return false;
}
}
}
To test it you should be able to copy-paste the code and run it in a solution. It is completely independent.
Thanks in advance!
You need to add this:
[Export ("initWithFrame:")]
in you cell like so
public class FamilyCollectionCell : UICollectionViewCell
{
public UILabel nameLabel;
[Export ("initWithFrame:")] // ADDED HERE
public FamilyCollectionCell (CGRect frame) : base (frame)
{
BackgroundColor = UIColor.Orange;
ContentView.Layer.BorderColor = UIColor.LightGray.CGColor;
ContentView.Layer.BorderWidth = 2.0f;
ContentView.BackgroundColor = UIColor.White;
ContentView.Transform = CGAffineTransform.MakeScale (0.8f, 0.8f);
nameLabel = new UILabel ();
nameLabel.Text = "name";
nameLabel.SizeToFit ();
nameLabel.Center = ContentView.Center;
ContentView.AddSubview (nameLabel);
}
[Export("custom")]
void Custom()
{
Console.WriteLine ("some code in the cell");
}
public override bool CanPerform (Selector action, NSObject withSender)
{
if (action == new Selector ("custom"))
return true;
else
return false;
}
}
Also that's an interesting way of adding in the collectionView into a viewcontroller. Is there any need for FamilyView? It might be better to add the navbar to the collectionview controller?
Using MonoTouch.Dialog I add a simple table and add a ScopeBar:
this.Style = UITableViewStyle.Plain;
this.EnableSearch = true;
this.AutoHideSearch = false;
this.SearchPlaceholder = "Search".t();
UISearchBar sb = TableView.TableHeaderView as UISearchBar;
if (sb != null)
{
sb.ScopeButtonTitles = new string[] { "Girl".t(),"Boy".t(),"All".t() };
sb.ShowsScopeBar = true;
sb.SizeToFit();
}
Looks good:
When I set the Section and give it a Title, the Section appears on top of the scope bar:
Section secMain = new Section("Top 100".t());
To do this, you need to change MonoTouch.Dialog.DialogViewController and make void SetupSearch() protected virtual.
Then, in your controller override the SetupSearch method with the code that follows. The downside to this approach is that you have to use a custom search delegate. But from seeing the response to some of your other questions, it appears you are already doing that.
protected override void SetupSearch()
{
SearchBar = new UISearchBar(new RectangleF (0, 0, TableView.Bounds.Width, 90))
{
Delegate = new MySearchBarDelegate(this),
Placeholder = "Search".t(),
ShowsScopeBar = true,
ScopeButtonTitles = new [] { "Girl".t(),"Boy".t(),"All".t() },
SelectedScopeButtonIndex = 0,
};
TableView.TableHeaderView = SearchBar;
}
public class MySearchBarDelegate : UISearchBarDelegate
{
MyViewController _container;
public SearchDelegate (MyViewController container)
{
_container = container;
}
public override void SelectedScopeButtonIndexChanged (UISearchBar searchBar, int index)
{
_container.SearchScopeChanged(searchBar, index);
}
public override void OnEditingStarted (UISearchBar searchBar)
{
searchBar.ShowsCancelButton = true;
_container.StartSearch ();
}
public override void OnEditingStopped (UISearchBar searchBar)
{
searchBar.ShowsCancelButton = false;
_container.FinishSearch ();
}
public override void TextChanged (UISearchBar searchBar, string searchText)
{
_container.PerformFilter (searchText ?? "");
}
public override void CancelButtonClicked (UISearchBar searchBar)
{
searchBar.ShowsCancelButton = false;
_container.SearchBar.Text = "";
_container.FinishSearch ();
searchBar.ResignFirstResponder ();
}
public override void SearchButtonClicked (UISearchBar searchBar)
{
_container.SearchButtonClicked (searchBar.Text);
}
}