0

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
            }
        }
    }
}
Coen Hacking
  • 13
  • 1
  • 7
  • Does it hit all the breakpoints? What does it currently look like? – Saamer May 30 '21 at 23:35
  • Oh, it displays, I get my blue background and white tiles with a grey outline, but I don't get them to fill the width of the screen – Coen Hacking May 31 '21 at 08:04
  • It looks correct according to the screenshot. What do you want it to display? – Saamer Jun 01 '21 at 10:38
  • Oh yeah, the code works, but I want all cells to fill the full width of the screen. – Coen Hacking Jun 02 '21 at 11:01
  • you want each cell to be on one line instead of multiple cells per line? If so, in your MessageListDataSource, you need to override this https://stackoverflow.com/a/54248862/11104068 making sure that there is only 1 cell per row – Saamer Jun 02 '21 at 18:04
  • I don't see that in the C# wrapper, but I did have some progress when implementing it in the MessageListLayout. I'm getting the behaviour I'd expect, but only after an update is called. – Coen Hacking Jun 02 '21 at 23:00

0 Answers0