For performance reasons I decided to write a custom ViewCell for my ListView in Xamarin Forms. For this ViewCell I've extended the ViewCellRenderer for iOS. In the GetCell-Method I create a new instance of an extended UITableViewCell which creates some labels and images and arranges them by some constraints on the anchors.
The ListView in Xamarin Forms has HasUnevenRows="True", but the rows are all cut-off at 44px. How can I get automatic height to the rows by the created content from the custom UITableViewCell?
Cause:
Actually, The auto height with HasUnevenRows=true works fine on iOS if not using a custom renderer.
If using a custom renderer, then it is up to the renderer to set the height of the cell, but in reality you have to do it in the GetHeightForRow override of the UITableViewSource.
This means you have to calculate the row height in UITableViewSource that you make that sets the height for a row in the GetHeightForRows override if you want to use custom viewcell render in the listView with uneven rows.
Solution:
Basically you have to make a custom ListView renderer. Then you have to get the UITableView's Source property (Control.Source in the list view renderer) and pass that to a new instance of a subclass of UITableVIewSource that you create, and then override all of the UITableViewSource methods so you can call into the original source methods when you don't want to change anything, but for the GetRowForHeight method, you would return the height you want for that row. E.G.:
[assembly: ExportRenderer(typeof(ListView), typeof(MyLVRenderer))]
public class MyLVRenderer : ListViewRenderer
{
//UITableViewSource originalSource;
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
UITableViewSource originalSource = (UIKit.UITableViewSource)Control.Source;
Control.Source = new MyLVSource(originalSource);
}
}
public class MyLVSource : UITableViewSource
{
UITableViewSource originalSource;
public MyLVSource(UITableViewSource origSource)
{
originalSource = origSource;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return originalSource.RowsInSection(tableview, section);
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
return originalSource.GetCell(tableView, indexPath);
}
public override nfloat GetHeightForFooter(UITableView tableView, nint section)
{
return originalSource.GetHeightForFooter(tableView, section);
}
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
nfloat origHeight = originalSource.GetHeightForRow(tableView, indexPath);
// calculate your own row height here
return origHeight + (indexPath.Row +1) * 10;
}
}
This is a example of increasing the row height by ten for every row. You have to calculate your own row height by yourself. I can share you the demo if you need.
Refer: viewcell
You should override LayoutSubviews. You should set renderer.NativeView.Frame to proper values.
Related
I have my own xib-file looking like this:
Now I programmatically add multiple instances of this XIB-View to my original View which is a UIStackView with full width inside of a UIScrollView which takes the whole screen size.
I want to achieve, that every instance of my xib-view has the whole displayWidth. This is what I tried:
myView.frame.size = CGSize(myView.frame.size.height, view.frame.size.width())
But I can't change the view's width with this code. How do I achieve to change the width?
Edit: This is how I add the views (it is in java because I am using multi-os-engine but It is almost exactly like in objective-c)
#Override
#Selector("viewDidLoad")
public void viewDidLoad() {
UIView checkBox = instantiateFromNib("CheckBoxView");
for (UIView v : checkBox.subviews()) {
if (v instanceof DLRadioButton) {
((DLRadioButton) v).setTitleForState(option.getName(), UIControlState.Normal);
}
}
configView.addArrangedSubview(checkBox);
}
private UIView instantiateFromNib(String name) {
return (UIView) UINib.nibWithNibNameBundle(name, null).instantiateWithOwnerOptions(null, null).firstObject();
}
I'm new at Xamarin development and I'm trying to display the list of comments for a specific ticket. The comments' body have different text size. I have a custom view TicketCommentRowUIView.xib:
using Foundation;
using System;
using UIKit;
using ObjCRuntime;
namespace Project.iOS
{
public partial class TicketCommentRowUIView : UIView
{
public TicketCommentRowUIView(IntPtr handle) : base(handle)
{
}
public static TicketCommentRowUIView CreateView(TicketCommentModel model)
{
TicketCommentRowUIView v = CreateView();
v.LoadModelInfo(model);
return v;
}
private static TicketCommentRowUIView CreateView()
{
var arr = NSBundle.MainBundle.LoadNib("TicketCommentRowUIView", null, null);
var v = Runtime.GetNSObject<TicketCommentRowUIView>(arr.ValueAt(0));
return v;
}
private void LoadModelInfo(TicketCommentModel model)
{
DateReply.Text = model.CreatedAt.ToShortDateString();
MessageReply.Text = model.Content;
ReplierImage.Image = UIImage.FromBundle("default_image_user");
}
}
}
And I'm adding that custom view to an stack view in TicketDetailViewController dinamically, but I don't know how to resize the custom view according its self size:
using Foundation;
using System;
using UIKit;
using System.Collections.Generic;
using System.Linq;
using CoreGraphics;
namespace Project.iOS
{
public partial class TicketDetailViewController : UIViewController
{
private nfloat TicketItemHeight = 200;
TicketModel _ticketItem;
List<TicketCommentModel> _ticketCommentsList;
TicketDetailPresenter _presenter;
public TicketDetailViewController (IntPtr handle) : base (handle)
{
_presenter = new TicketDetailPresenter(this);
}
public async override void ViewDidLoad()
{
base.ViewDidLoad();
ScrollView.LoadingStart();
await _presenter.LoadTicketComments(_ticketItem.Id);
FillComentsStackView();
ScrollView.LoadingComplete();
}
public void LoadMenuItem(TicketModel ticketItem)
{
_ticketItem = ticketItem;
}
public void FillComentsStackView()
{
foreach (TicketCommentModel item in _ticketCommentsList)
{
TicketCommentRowUIView itemView = TicketCommentRowUIView.CreateView(item);
itemView.HeightAnchor.ConstraintEqualTo(TicketItemHeight).Active = true;
CommentStackView.AddArrangedSubview(itemView);
}
}
}
}
My issue is that I don't know how to add an self size for each custom view, because the height of MessageReply UILabel it is variable... I put a constraint equals to 200 but when the text is too big it looks overlapped. And when it is a short text there is to much of blank space. Is there any way to do this?? In my android project I just set WrapContent layout params and it worked. But I can't find the way to apply that to the iOS project.
It seems you have set the Label's Line 0 to wrap its content.
Then you can set each item's constraint in your xib to make your custom view self size fit. From your screenshot you can set the left, top constraint of your image and top, left constraint of your DateReply, at last the left, top, right , bottom constraint of the MessageReply like:
Adjust the constant to feed your request. Moreover when you add the custom view in the TicketDetailViewController, there's no need to set the HeightAnchor.
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.
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;
}
}
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