Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I have two cells with different heights. When I remove the first one, the second one gets the height of the first one

Some context

In this screen you can add and remove people. Not all fields are required, so the cell height is dynamic.

The problem

If I add first a person with all the fields completed, then a second person with not all fields completed, and then I remove the first person, the second person takes the first place but with the layout of the first person

IMPORTANT

After I remove the first person, if I scroll up until the remaining cell is off screen, then it fixes itself

Code

This is the table source

namespace xXxx.xXxx.iOS
{
    public class SiniestroParticipantesSource : MvxTableViewSource
    {
        private readonly SiniestroParticipantesViewModel viewModel;

        public SiniestroParticipantesSource(UITableView tableView, SiniestroParticipantesViewModel viewModel)
            : base(tableView)
        {
            this.UseAnimations = true;
            this.AddAnimation = UITableViewRowAnimation.Top;
            this.RemoveAnimation = UITableViewRowAnimation.Middle;
            this.viewModel = viewModel;

            tableView.RegisterNibForCellReuse(UINib.FromName(PersonaDenunciaCellView.Key, NSBundle.MainBundle), PersonaDenunciaCellView.Key);
        }

        public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
        {
            tableView.DeselectRow(indexPath, true);
            var itemPersona = this.viewModel.Personas[indexPath.Section];
            this.viewModel.AgregarCommand.Execute(itemPersona);
        }

        protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
        {
            var cell = (PersonaDenunciaCellView)tableView.DequeueReusableCell(PersonaDenunciaCellView.Key, indexPath);
            return cell;
        }

        public override nint RowsInSection(UITableView tableview, nint section)
        {
            return 1;
        }

        public override nint NumberOfSections(UITableView tableView)
        {
            return this.viewModel.Personas.Count;
        }

        protected override object GetItemAt(NSIndexPath indexPath)
        {
            return this.viewModel.Personas[indexPath.Section];
        }
    }
}

This is the cell view

namespace xXxx.xXxx.iOS
{
    public partial class PersonaDenunciaCellView : MvxTableViewCell
    {
        public static readonly NSString Key = new NSString("PersonaDenunciaCellView");
        public static readonly UINib Nib;

        static PersonaDenunciaCellView()
        {
            Nib = UINib.FromName("PersonaDenunciaCellView", NSBundle.MainBundle);
        }

        protected PersonaDenunciaCellView(IntPtr handle) : base(handle)
        {

            this.DelayBind(() =>
            {
                var set = this.CreateBindingSet<PersonaDenunciaCellView, PersonaDenunciaItemViewModel>();

                set.Bind(btnRemove).To(vm => vm.RemoveCommand);

                set.Bind(lblNombre).To(vm => vm.Persona.Persona.Nombre);

                set.Bind(lblTipoDoc).To(vm => vm.Persona.Persona.TipoDoc.Descripcion);
                set.Bind(tipoDocVisibilityConst).For("Priority").To(vm => vm.Persona.Persona.Nrodoc).WithConversion("iOSVisibility", true);

                set.Bind(lblNrodoc).To(vm => vm.Persona.Persona.Nrodoc);
                set.Bind(nroDocVisibilityConst).For("Priority").To(vm => vm.Persona.Persona.Nrodoc).WithConversion("iOSVisibility", true);

                set.Bind(lblMailContacto).To(vm => vm.Persona.MailContacto);
                set.Bind(mailContactoVisibilityConst).For("Priority").To(vm => vm.Persona.MailContacto).WithConversion("iOSVisibility", true);

                set.Bind(lblTelContacto).To(vm => vm.Persona.TelContacto);
                set.Bind(telContactoVisibilityConst).For("Priority").To(vm => vm.Persona.TelContacto).WithConversion("iOSVisibility", true);

                set.Bind(lblLesionado).To(vm => vm.Persona.Lesionado).WithConversion("Lesionado");

                set.Bind(vehiculoVisibilityConst).For("Priority").To(vm => vm.Persona.Patente).WithConversion("iOSVisibility", true);
                set.Bind(viewVehiculo).For("Visibility").To(vm => vm.Persona.Patente).WithConversion("Visibility");
                set.Bind(lblPatente).To(vm => vm.Persona.Patente);
                set.Bind(lblCiaSeguroDesc).To(vm => vm.Persona.CiaSeguroDesc);

                set.Apply();
            });
        }
    }
}

Cell ViewModel

namespace xXxx.xXxx.Core.ViewModels.Items
{

    public class PersonaDenunciaItemViewModel:MvxViewModel
    {
        private readonly IMvxMessenger messenger;
        private readonly IUserInteraction userInteraction;

        public string Index { get; set; }

        public PersonaDenunciaItemViewModel (SiniestroCarga.PersonaDenuncia persona, IMvxMessenger messenger, IUserInteraction userInteraction, string index)
        {
            this.messenger = messenger;
            this.userInteraction = userInteraction;
            this.Persona = persona;
            this.Index = index;
        }

        private SiniestroCarga.PersonaDenuncia persona;
        public SiniestroCarga.PersonaDenuncia Persona
        {
            get
            {
                return this.persona;
            }
            set
            {
                this.persona = value;
                this.RaisePropertyChanged(() => this.Persona);
            }
        }

        private ICommand removeCommand;
        public ICommand RemoveCommand
        {
            get
            {
                return this.removeCommand = this.removeCommand ?? new MvxCommand(this.RemovePersona);
            }
        }

        private void RemovePersona()
        {
            this.userInteraction.Confirm("Are you sure?", () =>
            {
                this.messenger.Publish(new RemovePersonaDenunciaMessage(this, this.Persona, this.Index));
            }, null, "OK", "Cancel");
        }
    }

}

And that last messenger.Publish is subscribed to this on another ViewModel

OnRemovePersonaDenuncia = message =>
                {
                    var listPersonas = new ObservableCollection<PersonaDenunciaItemViewModel>(this.Personas);
                    listPersonas.Remove(this.Personas.First(p => p.Index == message.Index));
                    this.Personas = listPersonas;
                }

Updates

Changing the implementation of RemoveCommand to this

this.Personas.Remove(this.Personas.First(p => p.Index == message.Index));

Makes that, when I press the remove button, the simulator closes without any error. The application trace on Xamarin Studio shows this

*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.60.7/UITableView.m:1700

like image 380
Santiago Quiroga Avatar asked Sep 06 '16 18:09

Santiago Quiroga


1 Answers

iOS does not calculate the height of the cells correctly when the content height is dynamic. That´s very annoying but you can override GetHeightForRow and EstimatedHeight in SiniestroParticipantesSource to calculate the exact height of each cell depending on the data:

public override nfloat EstimatedHeight(UITableView tableView, NSIndexPath indexPath) => 
    GetHeightForRow(tableView, indexPath);

public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
    var data = (PersonaDenunciaItemViewModel)ItemsSource.ElementAt(indexPath.Row);
    var cell = (PersonaDenunciaCellView)tableView.DequeueReusableCell(PersonaDenunciaCellView.Key, indexPath);

    // TODO set the cell data manually (ignoring bindings)
    // i.e: **cell.lblNombre = data.Persona.Persona.Nombre**; // and every other field required

    cell.SetNeedsLayout();
    cell.LayoutIfNeeded();

    var size = cell.ContentView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
    return NMath.Ceiling(size.Height) + 1;
}

Here´s an example using the very same code.

like image 84
xleon Avatar answered Oct 03 '22 14:10

xleon