0

I am building an app with quite a number sub classed monotouch dialog RootElements . Part of customising the look and feel of the cell was adding a notification view on the far right of the cell based on boolean value bound from the viewmodel, as part of the validation of that specific cell.

This works great if all the cells never go out of view. But when scrolling comes into play, these UIViews added to the cells get mixed up.

I have tried overriding the CellKey getter and an assortment of other options to no avail, any assistance would be greatly appreciated. Thank you.

    public class PgpStyledRootElement : RootElement, IElementSizing, IValidatableElement
{
    float _height;

    bool _isRequired = false;

    UIView notificationView;

    protected IMvxMessenger _messenger;

    protected MvxSubscriptionToken _token;

    public WeakReference WeakNotificationView { get; set;}
    public UIView NotificationView
    {
        get
        {
            if (WeakNotificationView == null || !WeakNotificationView.IsAlive)
                return null;
            return WeakNotificationView.Target as UIView;
        }
    }

    private UITableViewCell _activeCell;
    public UITableViewCell ActiveCell
    {
        get 
        {
            if(_activeCell == null)
            {
                _activeCell = this.GetActiveCell ();
            }

            return _activeCell;
        }
        set
        {
            _activeCell = value;
        }
    }

    private bool _isValid = true;
    public bool IsValid 
    {
        get { return _isValid; } 
        set 
        {
            _isValid = value; 

            if (_isValid)
            {
                UIApplication.SharedApplication.InvokeOnMainThread (RemoveNotificationView);
            }
            else 
            {
                UIApplication.SharedApplication.InvokeOnMainThread (AddNotificationView);
            }
        } 
    }

    public PgpStyledRootElement(string caption, bool isRequired, Group group = null) : base(caption, group) {
    }

    public PgpStyledRootElement(string caption, bool isRequired, float height, bool fullPageWidth, Group group = null) : base(caption, group) {
        _height = height;
    }

    public PgpStyledRootElement(string caption, bool isRequired, float height, Group group = null, string defaultDetailText = null) : base(caption, group) {
        _height = height;
        _isRequired = isRequired;
        _messenger = Mvx.Resolve<IMvxMessenger> ();
        _token = _messenger.Subscribe<ScreenWillRotateMessage> (HandleScreenWillRotate);

    }

    public PgpStyledRootElement (string caption, bool isRequired, float height, Func<RootElement, UIViewController> createOnSelected) : base (caption, createOnSelected)
    {
        this.Sections = new List<Section> ();
        _height = height;

        _messenger = Mvx.Resolve<IMvxMessenger> ();
        _token = _messenger.Subscribe<ScreenWillRotateMessage> (HandleScreenWillRotate);
    }

    public PgpStyledRootElement(string caption,  float height, Group group = null) : base(caption, group) {
        _height = height;
    }

    protected override UITableViewCell GetCellImpl (UITableView tv)
    {
        var cell = base.GetCellImpl (tv);

        HandleInitialLoadValidation (cell);

        return cell;
    }

    protected override void UpdateCellDisplay (UITableViewCell cell)
    {
        base.UpdateCellDisplay (cell);

        if (cell != null) {
            cell.TextLabel.TextColor = CaptureListingsStyleConfig.DefaultTableCellTextColor;
            cell.SetNeedsLayout ();
        }
    }

    public void HandleInitialLoadValidation (UITableViewCell cell)
    {
        if (_isRequired) 
        {
            if (!IsValid)
            {
                AddInitialNotificationView (cell);
            }
        }
    }

    static void GetRequiredIndicatorFrame (UITableViewCell cell, out float indicatorPointX, out float indicatorHeight)
    {
        if (UIApplication.SharedApplication.StatusBarOrientation == UIInterfaceOrientation.Portrait || UIApplication.SharedApplication.StatusBarOrientation == UIInterfaceOrientation.PortraitUpsideDown) {
            indicatorPointX = cell.Frame.Width + 120;
            indicatorHeight = cell.Frame.Height;
        }
        else {
            indicatorPointX = cell.Frame.Width * 2 + 56;
            indicatorHeight = cell.Frame.Height;
        }
    }

    static void GetRequiredIndicatorFrameForRotationChange (UITableViewCell cell, out float indicatorPointX, out float indicatorHeight)
    {
        if (UIApplication.SharedApplication.StatusBarOrientation == UIInterfaceOrientation.Portrait || UIApplication.SharedApplication.StatusBarOrientation == UIInterfaceOrientation.PortraitUpsideDown) {
            indicatorPointX = cell.Frame.Width - 263;
            indicatorHeight = cell.Frame.Height;
        }
        else {
            indicatorPointX = cell.Frame.Width + 249;
            indicatorHeight = cell.Frame.Height;
        }
    }

    void HandleScreenWillRotate (ScreenWillRotateMessage obj)
    {
        RectangleF newFrame;

        if(NotificationView != null)
        {
            const int indicatorpointY = 0;
            const int indicatorWidth = 7;

            float indicatorPointX;
            float indicatorHeight;

            GetRequiredIndicatorFrameForRotationChange (ActiveCell, out indicatorPointX, out indicatorHeight);

            CreateNewNotificationViewFrame (out newFrame, indicatorpointY, indicatorWidth, indicatorPointX, indicatorHeight);

            NotificationView.Frame = newFrame;
        }
    }

    static void CreateNewNotificationViewFrame (out RectangleF newFrame, int indicatorpointY, int indicatorWidth, float indicatorPointX, float indicatorHeight)
    {
        newFrame = new RectangleF (indicatorPointX, indicatorpointY, indicatorWidth, indicatorHeight);
    }

    public void AddInitialNotificationView (UITableViewCell cell) 
    {
        if (cell != null) {

            const int indicatorpointY = 0;
            const int indicatorWidth = 7;

            float indicatorPointX;
            float indicatorHeight;

            GetRequiredIndicatorFrame (cell, out indicatorPointX, out indicatorHeight);

            if (Util.UserInterfaceIdiomIsPhone) {
                indicatorPointX = cell.Frame.Width - indicatorWidth;
            }

            AddViewToCell (cell, indicatorpointY, indicatorWidth, indicatorPointX, indicatorHeight);
        }
    }

    public void AddNotificationView () 
    {
        if (ActiveCell != null) {

            const int indicatorpointY = 0;
            const int indicatorWidth = 7;

            var indicatorPointX = ActiveCell.Frame.Width - indicatorWidth;
            var indicatorHeight = ActiveCell.Frame.Height;

            if (Util.UserInterfaceIdiomIsPhone) {
                indicatorPointX = ActiveCell.Frame.Width - indicatorWidth;
            }

            AddViewToCell (ActiveCell, indicatorpointY, indicatorWidth, indicatorPointX, indicatorHeight);
        }
    }

    void AddViewToCell (UITableViewCell cell, int indicatorpointY, int indicatorWidth, float indicatorPointX, float indicatorHeight)
    {
        if (NotificationView == null) {

            notificationView = new UIView (new RectangleF (indicatorPointX, indicatorpointY, indicatorWidth, indicatorHeight));
            notificationView.BackgroundColor = UIColor.Red;

            WeakNotificationView = new WeakReference (notificationView);
            cell.Add (NotificationView);
        }
    }

    public void RemoveNotificationView()
    {
        if(NotificationView != null)
        {
            NotificationView.RemoveFromSuperview ();
            WeakNotificationView = null; //This should technically not be necessary, but lets do it anyway
        }
    }


    #region IElementSizing implementation

    public float GetHeight (UITableView tableView, NSIndexPath indexPath)
    {
        if(_height == 0.0f)
            return CaptureListingsStyleConfig.DefaultTextCellHeight; //return CaptureListingsStyleConfig.DefaultRootCellHeight ();

        return _height;
    }

    #endregion

}
rohanjansen
  • 51
  • 1
  • 3

1 Answers1

0

Whenever a cell is required for display, then the DialogViewController calls the Element's GetCell method. This method then executes GetCellImpl followed by UpdateCellDisplay.

In the case of reuse, I suspect your RootElement is returning the reused Cell in GetCellImpl (because that's what the base class returns) and then not fully updating it. You could try modifying your UpdateCellDisplay method so that it updates the cell display to reflect the current state of your Element - i.e. adding/removing the notification display.

Stuart
  • 66,722
  • 7
  • 114
  • 165
  • Thanks for the reply Stuart. What we ended up doing is we pulled the mvvmcross source code from github, and added additional functionality to the RootElement source that would allow you to override the cellkey to provide unique reuseidentifiers if needed. – rohanjansen Jul 04 '14 at 11:08