I have Xamarin.iOS project, where is I use TableView
Here is tableView source code
public class ExperienceSourceNew: UITableViewSource
{
private UINavigationController primNav { get; set; }
private UITableView tableView { get; set; }
List<Experience> TableItems;
ExperienceViewControllerNew _owner;
static NSString cellIdentifier = new NSString("newcell_id");
public ExperienceSourceNew(List<Experience> items, ExperienceViewControllerNew owner, UINavigationController nav)
{
TableItems = items;
_owner = owner;
primNav = nav;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
if (TableItems.Count == 0)
{
var noDataLabel = new UILabel
{
Text = "No Experiences at your location at this time. Try to change destination",
TextColor = UIColor.Black,
TextAlignment = UITextAlignment.Center,
LineBreakMode = UILineBreakMode.WordWrap,
Lines = 0
};
tableview.BackgroundView = noDataLabel;
tableview.SeparatorStyle = UITableViewCellSeparatorStyle.None;
return TableItems.Count;
}
else
{
tableview.BackgroundView = null;
tableview.SeparatorStyle = UITableViewCellSeparatorStyle.SingleLine;
return TableItems.Count;
}
}
public override async void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
var selectedExperience = await ExperienceMethods.GetSelectedTour(TableItems[indexPath.Row].id);
if (selectedExperience == "Saved")
{
ExperienceDetailViewController ExperienceDetailController = primNav.Storyboard.InstantiateViewController("ExperienceDetailViewController") as ExperienceDetailViewController;
primNav.PushViewController(ExperienceDetailController, true);
}
else
{
UIAlertController okAlertController = UIAlertController.Create("", "Cannot select this experience", UIAlertControllerStyle.Alert);
okAlertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
}
tableView.DeselectRow(indexPath, true);
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = (ExperienceCellNew)tableView.DequeueReusableCell(cellIdentifier, indexPath);
Experience item = TableItems[indexPath.Row];
cell.UpdateCell(item);
return cell;
}
I use custom Cell for TableView, so here is code for it
public ExperienceCellNew (IntPtr handle) : base (handle)
{
}
internal void UpdateCell(Experience experience)
{
var image_url = "https://xplorpal.com/" + experience.cover_image.img_path + "/150x150/" + experience.cover_image.img_file;
ExperienceTitleNew.Text = experience.title;
ExperiencePriceNew.Text = "$" + experience.price;
NSUrlSession session = NSUrlSession.SharedSession;
var dataTask = session.CreateDataTask(new NSUrlRequest(new NSUrl(image_url)), (data, response, error) =>
{
if (response != null)
{
DispatchQueue.MainQueue.DispatchAsync(() =>
{
ExperienceImageNew.Image = UIImage.LoadFromData(data);
});
}
});
dataTask.Resume();
}
}
My problem, that images not appears on View load, I have text in labels, but don't have images. If I scroll TableView, images are appears in cells.
Where can be problem and how to solve it?
Table Views with large number of rows display only a small fraction of their total items at a given time.It's efficient for table views to ask for only the cells for row that are being displayed Therefor on scrolling the table view, the cells get re-used and the data within the cell is repopulated.
Since you are using a downloaded image it appears to be slow.
You can try loading the image of the exact size as the image view within the cell.
This is similar to your issue
And also assigning the image to imageview on main thread and make it synchronous as UI changes require synchronous access to main thread.
As an alternative, you can use FFImageLoading which will let you do this
ImageService.LoadUrl(image_url).Into(ExperienceImageNew);
Related
I'm trying to make an app for people of my student dancing association to find a dance partner. It's been a fun project during corona. Everything is done in ASP Core 5 and Xamarin Forms, and it's almost ready for testing. However, I'm having some trouble with the chat UI. When I use a CollectionView in Forms, it loads very slowly, due to rendering performance (at least on Android, iOS is generally much faster). To solve that I tried creating it as a view and use native renderers for iOS and Android. With Android, I simply used a NuGet package (XamarinLibrary.Xamarin.AndroidX.ChatKit), but for iOS I could find no such package. There's a native SwiftUI package (MessageKit), though I don't know how to wrap that (and the documentation is a lot). So I started to instead write it in Xamarin iOS. My test loads really quickly, although I'm now struggling with the layout of the Collection and the ViewCell. Could someone show me how to create message cells that fill the horizontal space?
Thanks in advance! :)
This is what it looks like right now:
Screenshot
[UPDATE] I partially solved the issue by adding this (though it does not work in the constructor):
Screenshot 2
public override bool ShouldInvalidateLayoutForBoundsChange(CGRect newBounds) {
MinimumLineSpacing = 10;
MinimumInteritemSpacing = 10;
SectionInset = new UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10);
ItemSize = new CGSize(width: (newBounds.Width - 20), height: 100);
return true;
}
My code currently (the data source isn't correct yet):
public class MessageListLayout : UICollectionViewFlowLayout {
public MessageListLayout() {
ItemSize = new CGSize(UIScreen.MainScreen.Bounds.Size.Width, 50);
EstimatedItemSize = AutomaticSize;
}
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);
}
}
public class MessageListDataSource : UICollectionViewDataSource {
private string CellId { get; set; }
#region Computed Properties
public UICollectionView CollectionView { get; set; }
public List<int> Numbers { get; set; } = new List<int>();
#endregion
#region Constructors
public MessageListDataSource(UICollectionView collectionView, string cellId) {
// Initialize
CollectionView = collectionView;
CellId = cellId;
// Init numbers collection
for (int n = 0; n < 100; ++n) {
Numbers.Add(n);
}
}
#endregion
#region Override Methods
public override nint NumberOfSections(UICollectionView collectionView) {
// We only have one section
return 1;
}
public override nint GetItemsCount(UICollectionView collectionView, nint section) {
// Return the number of items
return Numbers.Count;
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) {
// Get a reusable cell and set {~~it's~>its~~} title from the item
var cell = collectionView.DequeueReusableCell(CellId, indexPath) as MessageListViewCell;
cell.Text = Numbers[(int)indexPath.Item].ToString();
return cell;
}
public override bool CanMoveItem(UICollectionView collectionView, NSIndexPath indexPath) {
// We can always move items
return true;
}
public override void MoveItem(UICollectionView collectionView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath) {
// Reorder our list of items
var item = Numbers[(int)sourceIndexPath.Item];
Numbers.RemoveAt((int)sourceIndexPath.Item);
Numbers.Insert((int)destinationIndexPath.Item, item);
}
#endregion
}
So this should be the base message bubble.
public class MessageListViewCell : UICollectionViewCell {
UIImageView imageView;
public string Text { get; set; }
[Export("initWithFrame:")]
public MessageListViewCell(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(1, 1);
ContentView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;
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;
}
}
[Export("custom")]
public void Custom() {
// Put all your custom menu behavior code here
Console.WriteLine("custom in the cell");
}
public override bool CanPerform(Selector action, NSObject withSender) {
if (action == new Selector("custom"))
return true;
else
return false;
}
}
[assembly: ExportRenderer(typeof(MessagesListControl), typeof(MessagesListControlRenderer))]
namespace Vinder.iOS.UI.Renderers {
public class MessagesListControlRenderer : ViewRenderer<MessagesListControl, UICollectionView> {
static readonly NSString messageListViewCell = new("MessageListViewCell");
private UICollectionView collectionView { get; set; }
protected override void OnElementChanged(ElementChangedEventArgs<MessagesListControl> e) {
base.OnElementChanged(e);
if (e.OldElement != null) {
// Unsubscribe to events
}
if (e.NewElement != null) {
if (Control == null) {
collectionView = new UICollectionView(new CGRect(0, 0, UIScreen.MainScreen.Bounds.Size.Width, 300), new MessageListLayout());
collectionView.RegisterClassForCell(typeof(MessageListViewCell), messageListViewCell);
collectionView.BackgroundColor = UIColor.Blue; // blue is easy for seeing the correct bounds
collectionView.DataSource = new MessageListDataSource(collectionView, messageListViewCell);
SetNativeControl(collectionView);
}
// Subscribe to events
}
}
}
}
We've created a TableViewController so that a user can enable/disable different notification types. After the initial load, everything is fine until the user scrolls to the bottom then back to the top and the first 1-2 table view cells aren't displaying correctly.
Example:
https://imgur.com/7n2VpTo
Typically deleting and recreating the view controller and xib/cs files fixes this, but was wondering if any one knows the cause of this.
Controller Code:
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
return 305;
}
public override nint NumberOfSections(UITableView tableView)
{
return 1;
}
public override nint RowsInSection(UITableView tableView, nint section)
{
return _numRows;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell(Setting.Key) as Setting;
if (cell == null)
{
var values = _settings.Where(s => s.Index == indexPath.Row).FirstOrDefault();
cell = new Setting(values);
var views = NSBundle.MainBundle.LoadNib(Setting.Key, cell, null);
cell = Runtime.GetNSObject(views.ValueAt(0)) as Setting;
}
cell.SelectionStyle = UITableViewCellSelectionStyle.None;
cell?.PopulateData(_settings.Where(s => s.Index == indexPath.Row).FirstOrDefault(), this);
return cell;
}
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
}
Cell Code:
public partial class Setting : UITableViewCell
{
public static readonly NSString Key = new NSString("Setting");
public static readonly UINib Nib;
public SettingModel Values { get; set; }
static Setting()
{
Nib = UINib.FromName("Setting", NSBundle.MainBundle);
}
protected Setting(IntPtr handle) : base(handle) { }
public Setting() : base() {
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
}
public void PopulateData(SettingModel values)
{
//logic...
}
}
Upgrading to Visual Studio for Mac 8.6.5 resolved the issue.
I am making an app for iOS using Xamarin I had some trouble with UITableViews and found this tutorial: https://learn.microsoft.com/en-us/xamarin/ios/user-interface/controls/tables/populating-a-table-with-data
I created a new project and followed what they did, but it doesn't work for me. I must be doing something wrong, but I can't figure out what.
Could someone take a look and see why the TableView doesn't show up?
BasicViewController:
public class BasicTableViewController : UIViewController
{
private UIView view;
public override void ViewDidLoad()
{
base.ViewDidLoad();
var table = new UITableView(View.Bounds);
table.Source = new BasicTableSource("aaaaaaaaa", "bbbbb", "cccccccc", "ddddddddddddd", "eeeee");
Add(table);
}
public override void LoadView()
{
view = new UIView()
{
BackgroundColor = UIColor.White,
};
View = view;
}
}
BasicTableViewSource:
public class BasicTableSource : UITableViewSource
{
private readonly string[] items;
public BasicTableSource(params string[] items)
{
this.items = items;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell("cell");
if (cell == null)
cell = new UITableViewCell(UITableViewCellStyle.Default, "cell");
cell.TextLabel.Text = items[indexPath.Row];
return cell;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return items.Length;
}
}
AppDelegate:
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow Window { get; set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
Window.RootViewController = new BasicTableViewController();
Window.MakeKeyAndVisible();
return true;
}
}
What the simulator looks like when running this project:
What the simulator looks like
Cause:
When you override the method LoadView.It seems that you forgot to set the Frame of view.So the size of view will with Height as 0 and Width as 0.
Solution:
Set the frame of view as the size of screen.
public override void LoadView()
{
view = new UIView()
{
Frame = UIScreen.MainScreen.Bounds,
BackgroundColor = UIColor.White,
};
View = view;
}
I have a simple custom multiline element whose linebreak mode i've set to wordwrap, and lines property i set to 7. My text seems to wrap just fine, but the cell height stays the same, so the text extends the bounds of the cell. I've tried increasing the frame of the cell and overriding GetHeight, but it doesn't seem to work in either case. Also, it doesn't seem my GetHeight is even been called.
Any one face something similar? Or any ideas on what the issue might be?
public class DisclaimerMultilineElement : MultilineElement
{
public DisclaimerMultilineElement (string caption) : base(caption)
{
}
public DisclaimerMultilineElement (string caption, string value) : base(caption, value)
{
}
public DisclaimerMultilineElement (string caption, NSAction tapped) : base(caption, tapped)
{
}
public override float GetHeight (UITableView tableView, NSIndexPath indexPath)
{
var ht = base.GetHeight (tableView, indexPath);
//return base.GetHeight (tableView, indexPath);
return 400.0f;
}
public override UITableViewCell GetCell (MonoTouch.UIKit.UITableView tv)
{
UITableViewCell cell = base.GetCell (tv);
var frame = cell.Frame;
frame.Height = 300f;
//cell.Frame = new System.Drawing.RectangleF(frame.X, frame.Y, frame.Width, frame.Height);
cell.BackgroundColor = UIColor.Clear;
cell.TextLabel.Font = UIFont.BoldSystemFontOfSize (12);
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
cell.TextLabel.Lines = 7;
cell.TextLabel.TextAlignment = UITextAlignment.Left;
cell.TextLabel.Text = Value;
return cell;
}
}
Make sure that UnevenRows is set to true in your Root element.
You need to override GetHeightForRow in your UITableViewSource and return the appropriate height.
I have the following code set up:
TableView = new UITableView();
TableView.Source = new DataSource();
m_MainScroll.Add(view); // scrollview area
TableView.ReloadData ();
DataSource is simple:
class DataSource : UITableViewSource {
public DataSource ()
{
}
public override int NumberOfSections (UITableView tableView)
{
return 1;
}
public override int RowsInSection (UITableView tableview, int section)
{
return 10;
}
// Customize the appearance of table view cells.
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
return new UITableViewCell();
}
}
Basically GetCell is never called and I never see the table show up anyway.
Any ideas why?
edit:
Some more potentially relevant code:
(in ViewDidLoad):
m_MainScroll = new UIScrollView(new RectangleF(0, 0, 320, 372));
m_MainScroll.ContentSize = new SizeF(300, ScrollerHeight);
m_MainScroll.ShowsVerticalScrollIndicator = true;
m_MainScroll.DraggingStarted += DragStarted;
AddComponent(m_MainScroll);
UIToolbar bar = new UIToolbar(new RectangleF(0, 372, 320, 44));
View.Add(bar);
UIBarButtonItem barBut = new UIBarButtonItem();
barBut.Style = UIBarButtonItemStyle.Bordered;
barBut.Title = "Next";
NextButton = barBut;
UIBarButtonItem flexer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
UIBarButtonItem []items = new UIBarButtonItem[2];
items[0] = flexer;
items[1] = barBut;
bar.Items = items;
AddComponent(bar);
public void AddComponent(UIView view)
{
m_Components.Add(view);
View.Add(view);
}
Where m_Components is just a list of UIView and is really only used to defocus controls which have the keyboard on.