I have a UICollectionView with
a custom layout that displays the items in a horizontal row
a custom UICollectionViewCell which contains a number of subviews - an image
and some labels.
Multiple instances of the UICollectionView are displayed vertically in a UITableView.
On iOS 8.1, all works well. Testing on iOS 7.1 though I can not drag the collection unless my finger is in the top third of the row (indicated by the yellow rectangle in the picture).
Note that the UITableView scrolls correctly vertically.
Any suggestions?
Thanks in advance, and apologies for the C#.
// The Layout
public sealed class SingleRowLayout : UICollectionViewFlowLayout
{
public SingleRowLayout()
{
this.ItemSize = new CGSize(50, 120); // 120 is the full cell height
this.ScrollDirection = UICollectionViewScrollDirection.Horizontal;
}
}
// The UICollectionViewCell
public sealed class StickerViewCell : UICollectionViewCell
{
[Export ("initWithFrame:")]
public StickerViewCell (CGRect frame) : base (frame)
{
var top = 0f;
this.imageView = new UIImageView();
this.imageView.ContentMode = UIViewContentMode.ScaleAspectFit;
this.imageView.Frame = new CGRect(
new CGPoint(GridDimensions.ImageInternalSidePadding, top),
new CGSize(GridDimensions.StickerWidth - (GridDimensions.ImageInternalSidePadding * 2), GridDimensions.StickerHeight)
);
top = (float)this.imageView.Frame.Height
this.sequenceTextView = this.createTextView(ref top);
this.dateTextView = this.createTextView(ref top);
this.sensationTextView = this.createTextView(ref top);
ContentView.AddSubview(this.imageView);
ContentView.AddSubview(this.sequenceTextView);
ContentView.AddSubview(this.sensationTextView);
ContentView.AddSubview(this.dateTextView);
}
private UILabel createTextView(ref float top)
{
var result = new UILabel();
result.AdjustsFontSizeToFitWidth = true;
result.Frame = new CGRect(
new CGPoint(2, top),
new CGSize(GridDimensions.StickerWidth - (2 * 2), GridDimensions.TextHeight)
);
top += GridDimensions.TextHeight + GridDimensions.TextFieldSpacing;
return result;
}
}
Related
I have a custom UIView class, where I would like to add a bunch of subviews. This should be a simple task but I can't seem to figure out why my views are not showing up on the UI.
The examples and tutorials out there are pretty straight forward and I believe I have implemented my code correctly. I have added a breakpoint at AddSubview and the container has a size.
The UIViewController class already has a background image added it via the storyboard.
Can someone help me see what I'm probably missing? I know it's something small.
Below is my code:
The custom view class
public class RotaryWheel : UIView
{
int numberOfSections;
public RotaryWheel(CGRect frame,int sections): base(frame)
{
numberOfSections = sections;
DrawWheel();
}
public void DrawWheel()
{
// derive the center x and y
float centerX = (float)(Frame.Width / 2);
float centerY = (float)(Frame.Height / 2);
container = new UIView();
container.Frame = new RectangleF(centerX, centerY, 100, 100);
container.BackgroundColor = UIColor.White;
AddSubview(container);
}
}
The UIViewController class where I initialize the view
public partial class HomePageController : UIViewController
{
public override void LoadView()
{
base.LoadView();
rotaryWheel = new RotaryWheel(new CGRect(20f, (float)(View.Frame.Height / 2), (float)View.Frame.Size.Width, (float)View.Frame.Height / 2f), 7);
View.AddSubview(rotaryWheel);
}
}
First at all, refer to loadView(). As the documentation mentioned
If you want to perform any additional initialization of your views, do so in the viewDidLoad() method.
We should never initialize subviews in LoadView, so I try to move that code to viewDidLoad
public override void ViewDidLoad()
{
base.ViewDidLoad();
RotaryWheel rotaryWheel = new RotaryWheel(new CGRect(20f, (float)(View.Frame.Height / 2), (float)View.Frame.Size.Width, (float)View.Frame.Height / 2f), 7);
View.AddSubview(rotaryWheel);
}
But it looks like this, the subviews doesn't locate as expected.
After moving that code to ViewDidAppear, the problem disappeared.
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
if(rotaryWheel == null){
rotaryWheel = new RotaryWheel(new CGRect(20f, (float)(View.Frame.Height / 2), (float)View.Frame.Size.Width, (float)View.Frame.Height / 2f), 7);
View.AddSubview(rotaryWheel);
}
}
Summary:
The real size of View comes out in method ViewDidAppear. If you don't use autoLayout , you should manage the View Frame carefully.
And about why you didn't see the subview, I guess you selected the iphone4 ,iPhone 5 or iPhone5S simulator for test , the screen width is 320, you create RotaryWheel with X= 20 , centerX is 300 in method LoadView, so it is displayed out of screen.
This may be a fairly simple solution but none of the suggestions here work for me. I have a UIScrollView inside a UITableViewCell. I'm adding dynamic images from a list into the scrollview like this.
public void InitViews()
{
scrollViewThumbNails.ContentSize =
new SizeF((float)scrollViewThumbNails.Frame.Size.Width/listCount * listCount,
(float)scrollViewThumbNails.Frame.Size.Height);
for (int i = 0; i < listCount; i++)
{
var imageView = new UIImageView
{
Frame = new RectangleF((float)i * (float)scrollViewThumbNails.Frame.Size.Width / listCount, 0,
(float)scrollViewThumbNails.Frame.Size.Width / listCount, (float)scrollViewThumbNails.Frame.Size.Height),
UserInteractionEnabled = true,
ContentMode = UIViewContentMode.ScaleAspectFit
};
//call method to load the images
var index = i;
imageView.SetImage(
url: new NSUrl(allItems[i].AbsoluteUri),
placeholder: UIImage.FromFile("placeholder.png"),
completedBlock: (image, error, type, url) =>
{
//when download completes add it to the list
if (image != null)
{
allImages.Add(image);
scrollViewThumbNails.AddSubview(imageView);
}
});
}
I found a suggestion here which advices to set the content size of the scrollview, based on the total number of subviews in ViewDidAppear like this:
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
CGRect contentRect = CGRect.Empty;
foreach(UIImageView view in scrollViewThumbNails.Subviews)
{
contentRect = CGRect.Union(contentRect,view.Frame);
}
scrollViewThumbNails.ContentSize = contentRect.Size;
}
The imageViews are still spaced out and do not start from the edge of the screen/scrollview as shown in my screen shot below. I would like for the first image to always be positioned at the origin of the scrollview and wouldn't want the spacing between each image. How can I adjust the scrollview based on the size of its contents?
Can someone show me what I'm missing? Thanks.
I found a solution here where I calculate the total height and width of all the views and assign that as the content size of the scroll view in ViewDidLayoutSubviews:
Solution
public override void ViewDidLayoutSubviews ()
{
base.ViewDidLayoutSubviews ();
try{
float scrollViewHeight = 0.0f;
float scrollViewWidth = 0.0f;
foreach(UIImageView view in scrollViewThumbnails.Subviews)
{
scrollViewWidth += (float)view.Frame.Size.Width;
scrollViewHeight += (float)view.Frame.Size.Height;
}
scrollViewThumbnails.ContentSize = new CGSize(scrollViewWidth, scrollViewHeight);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message+ex.StackTrace);
}
}
I've to do a ViewController with autolayouts in scrollView, but here is few problems:
public SomeVC() : UIViewController
{
_mainScrollView = new UIScrollView {
ShowsHorizontalScrollIndicator = false,
ShowsVerticalScrollIndicator = true,
BackgroundColor = UIColor.Clear,
ScrollEnabled = true,
AutoresizingMask = UIViewAutoresizing.FlexibleHeight,
TranslatesAutoresizingMaskIntoConstraints = true
};
_userDataTableView = new UITableView(CGRect.Empty, UITableViewStyle.Grouped);
_userDataTableView.LayoutIfNeeded();
_saveButton = new UIButton();
_menuTableView = new UITableView(CGRect.Empty, UITableViewStyle.Grouped);
_menuTableView.LayoutIfNeeded();
_logoutButton = new UIButton();
}
public override void LoadView()
{
base.LoadView();
View = _mainScrollView;
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
Add(_userDataTableView);
Add(_saveButton);
Add(_menuTableView);
Add(_logoutButton);
_mainScrollView.AddConstraints(
_userDataTableView.AtTopOf(View),
_userDataTableView.AtLeftOf(View),
_userDataTableView.AtRightOf(View),
_userDataTableView.Height().EqualTo(_userDataTableView.ContentSize.Height),
_saveButton.Below(_userDataTableView, 20),
_saveButton.AtLeftOf(_mainScrollView, 10),
_saveButton.AtRightOf(_mainScrollView, 10),
_saveButton.Height().EqualTo(44),
_menuTableView.Below(_saveButton, 20),
_menuTableView.AtLeftOf(_mainScrollView),
_menuTableView.AtRightOf(_mainScrollView),
_menuTableView.Height().EqualTo(_menuTableView.ContentSize.Height),
_logoutButton.Below(_menuTableView, 20),
_logoutButton.AtLeftOf(_mainScrollView, 10),
_logoutButton.AtRightOf(_mainScrollView, 10),
_logoutButton.Height().EqualTo(44)
);
_mainScrollView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
}
In fact, it works, but contents width is about half of screen width, and scrolling not working. How to get it works?
As far as I understand, the problem is - _mainScrollView.ContentSize, but how and where should I set it, when using autolayouts?
If your views doesn't exceed the screen you will not be able to do scroll. if you only have something like this :
// Create containers
contentView = new UIView();
scrollView = new UIScrollView { contentView };
Add(scrollView);
contentView.AddSubviews(logo, user, password, loginButton);
// Auto layout
View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
View.AddConstraints(scrollView.FullWidthOf(View));
View.AddConstraints(scrollView.FullHeightOf(View));
View.AddConstraints(
contentView.WithSameWidth(View),
contentView.WithSameHeight(View).SetPriority(UILayoutPriority.DefaultLow)
);
scrollView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
scrollView.AddConstraints(contentView.FullWidthOf(scrollView));
scrollView.AddConstraints(contentView.FullHeightOf(scrollView));
// very important to make scrolling work
var bottomViewConstraint = contentView.Subviews.Last()
.AtBottomOf(contentView).Minus(20);
contentView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
contentView.AddConstraints(
logo.AtTopOf(contentView),
logo.WithRelativeWidth(contentView, 0.8f),
logo.WithSameCenterX(contentView),
logo.WithRelativeHeight(contentView, 0.3f),
user.Below(logo, 50),
user.WithRelativeWidth(logo, 0.8f),
user.WithSameCenterX(logo),
password.Below(user),
password.WithSameWidth(user),
password.WithSameCenterX(user),
loginButton.Below(password, 50),
loginButton.WithRelativeWidth(password, 0.9f),
loginButton.Height().EqualTo(50),
loginButton.WithSameCenterX(password)
);
contentView.AddConstraints(bottomViewConstraint);
}
I'm using this package and it works perfectly like if I was using a stackLayout inside a scrollview in xamarin.forms I think is the perfect behaviour.
Xamarin.IQKeyboardManager from Nuget
Also, if you want to center your content view inside the scrollview you will need to add this:
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
var scrollViewBounds = scrollView.Bounds;
var containerViewBounds = contentView.Bounds;
var scrollViewInsets = UIEdgeInsets.Zero;
scrollViewInsets.Top = scrollViewBounds.Size.Height / 2.0f;
scrollViewInsets.Top -= contentView.Bounds.Size.Height / 2.0f;
scrollViewInsets.Bottom = scrollViewBounds.Size.Height / 2.0f;
scrollViewInsets.Bottom -= contentView.Bounds.Size.Height / 2.0f;
scrollViewInsets.Bottom += 1;
scrollView.ContentInset = scrollViewInsets;
}
And thats all, doesn't matter how your contentView is . You will have a centered contetnview inside a scrollview and a manager to capture the keyboard events and adapt your view to this event .
Solution found:
Firstly:
_userDataTableView.AtLeftOf(View),
_userDataTableView.AtRightOf(View),
is not valid, instead of this, we should use:
_userDataTableView.AtLeftOf(View),
_userDataTableView.WithSameWidth(View),
if we want margin, we just add
_userDataTableView.WithSameWidth(View).Minus(MARGIN)
And the last thing we have to do:
_logoutButton.Height().EqualTo(44),
_logoutButton.Bottom().EqualTo().BottomOf(_mainScrollView).Plus(10)
last line is very important. It says to scrollView right content size.
Stuart's N-06 Books sample is good for getting basic understanding about using MvxSimpleTableViewSource.
[Register("FirstView")]
public class FirstView : MvxViewController
{
public override void ViewDidLoad()
{
View = new UIView(){ BackgroundColor = UIColor.White};
base.ViewDidLoad();
// ios7 layout
if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
EdgesForExtendedLayout = UIRectEdge.None;
var textField = new UITextField(new RectangleF(10, 10, 300, 40));
Add(textField);
var tableView = new UITableView(new RectangleF(0, 50, 320, 500), UITableViewStyle.Plain);
Add(tableView);
tableView.RowHeight = 88;
var source = new MvxSimpleTableViewSource(tableView, BookCell.Key, BookCell.Key);
tableView.Source = source;
var set = this.CreateBindingSet<FirstView, Core.ViewModels.FirstViewModel>();
set.Bind(textField).To(vm => vm.SearchTerm);
set.Bind(source).To(vm => vm.Results);
set.Apply();
tableView.ReloadData();
}
}
But how can resize the tableview's height according it's content once it loads data?
Not entirely sure what you want to do... Normally in an iOS UI, the tableview size is fixed regardless of its content.
However, if you did want to resize the table then you could:
Inherit from MvxTableViewSource or UITableView and provide some logic there
Or add a binding in your class to some View property TableCount, bind that property and then implement the sizing logic there. Something like:
set.Bind(this).For(v => v.TableCount).To(vm => vm.Results.Count);
private int _tableCount
public int TableCount {
get { return _tableCount; }
set {
// implement your sizing animations here (maybe animate constraints?)
}
}
Just to add to Stuart's answer, here is an example for the frame of the table:
int _tableHeight;
public int TableHeight
{
get { return _tableHeight; }
set
{
_tableHeight = value;
_myPlayers.Frame = _tableHeight > 0 ? new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width, Dimens.TableRowHeight * _tableHeight) : new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width, Dimens.TableRowHeight * BusinessConstants.GetBiggestPositionCount());
_myPlayers.ReloadData();
}
}
I've got the following code that i've adapted from the Xamarin MobileCRM example:
[assembly: ExportCell(typeof(ListTextCell), typeof(ListTextCellRenderer))]
namespace Manager.iOS
{
class ListTextCellRenderer : TextCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableView tv)
{
var cellView = base.GetCell(item, tv);
cellView.TextLabel.Lines = 0;
return cellView;
}
}
}
Im wanting to have it that the TextLabel will stetch/wrap when it contains more text than the width of the device and therefore the rows will grow in size with it. When I set the Lines = 0; it wraps the label but the row height stays the same so every row just overlaps each other. How do I grow the row height as well?
Thanks
In your tableView source you will want to override the following method
public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
var text = new NSString(yourTextHere);
var size = text.StringSize(UIFont.FromName("FontName", fontSize), new SizeF(tableView.Frame.Width, 70)); // The 70 can be any number, the width is the important part
return size.Height + 16; // 16 is for padding
}
You may have to set HasUnevenRows or the RowHeight on your TableView