UIActivityIndicatorView.StartAnimating overriding UIActivityIndicatorView.Hidden binding

拈花ヽ惹草 提交于 2020-01-05 03:23:05

问题


I am creating a UIActivityIndicatorView in my Controller.ViewDidLoad

UIActivityIndicatorView spinner = new UIActivityIndicatorView();
spinner.StartAnimating();
spinner.Hidden = true;
this.Add(spinner);

Then I am binding it with MVVMCross

var set = this.CreateBindingSet<TipView, TipViewModel>();
set.Bind(spinner).For(v => v.Hidden).To(vm => vm.IsBusy).WithConversion("Inverse");

When the View initially loads the UIActivityIndicatorView is spinning and visible. This is incorrect as the IsBusy property is being explicitly set to False in the ViewModel's Init(). I can see this happening and I can see the Converter invert the value.

I know the binding is properly connected because if I fire a command that updates the IsBusy property the Indicator is shown and hidden as I would expect. It is just the initial state that is incorrect.

The StartAnimating method seems to cause the Hidden flag to be overridden. If I do not call StartAnimating the Indicator hides and shows as expected. Of course that means I have a non animating Indicator.

I could get a WeakReference to the VM, listen to PropertyChanged and call StartAnimating but that seems a bit rubbish.

Does anyone have any better ideas?


回答1:


Some options you can do:

  1. Subscribe to PropertyChanged changes and write custom code in the event handler (as you suggest in your question)

  2. Inherit from UIActivityIndicatorView and write a public get;set; property which provides the composite functionality (calling Start and Hidden) in the set handler

    public class MyIndicatorView : UIActivityIndicatorView {
          // ctors
    
         private bool _superHidden;
         public bool SuperHidden {
            get { return _supperHidden; }
            set { _superHidden = value; if (!value) StartAnimating() else StopAnimating(); Hidden = value; }
         }
    }
    
  3. Provide a View public get;set; property and put the composite functionality in that (e.g. set.Bind(this).For(v => v.MyAdvanced)...

         private bool _myAdvanced;
         public bool MyAdvanced {
            get { return myAdvanced; }
            set { myAdvanced = value; if (!value) _spinner.StartAnimating() else _spinner.StopAnimating(); _spinner.Hidden = value; }
         }
    
  4. Write a custom binding for Hidden which replaces the default functionality and contains the combined Start and Hidden calls (for more on custom bindings, there's a couple of N+1 tutorials)




回答2:


After reading @slodge's reply I went down the road of Weak Event Listener and ran the code to Hide and StartAnimating in the View. Having copy and pasted that approach 3 times I realised something had to change so I implemented his 4th suggestion and wrote a Custom Binding. FWIW here is that custom binding

/// <summary>
/// Custom Binding for UIActivityIndicator Hidden. 
/// This binding will ensure the indicator animates when shown and stops when hidden
/// </summary>
public class ActivityIndicatorViewHiddenTargetBinding : MvxConvertingTargetBinding
{
    /// <summary>
    /// Initializes a new instance of the <see cref="ActivityIndicatorViewHiddenTargetBinding"/> class.
    /// </summary>
    /// <param name="target">The target.</param>
    public ActivityIndicatorViewHiddenTargetBinding(UIActivityIndicatorView target)
        : base(target)
    {
        if (target == null)
        {
            MvxBindingTrace.Trace(
                                MvxTraceLevel.Error,
                                "Error - UIActivityIndicatorView is null in ActivityIndicatorViewHiddenTargetBinding");
        }
    }

    /// <summary>
    /// Gets the default binding mode.
    /// </summary>
    /// <value>
    /// The default mode.
    /// </value>
    public override MvxBindingMode DefaultMode
    {
        get { return MvxBindingMode.OneWay; }
    }

    /// <summary>
    /// Gets the type of the target.
    /// </summary>
    /// <value>
    /// The type of the target.
    /// </value>
    public override System.Type TargetType
    {
        get { return typeof(bool); }
    }

    /// <summary>
    /// Gets the view.
    /// </summary>
    /// <value>
    /// The view.
    /// </value>
    protected UIActivityIndicatorView View
    {
        get { return Target as UIActivityIndicatorView; }
    }

    /// <summary>
    /// Sets the value.
    /// </summary>
    /// <param name="target">The target.</param>
    /// <param name="value">The value.</param>
    protected override void SetValueImpl(object target, object value)
    {
        var view = (UIActivityIndicatorView)target;
        if (view == null)
        {
            return;
        }

        view.Hidden = (bool)value;

        if (view.Hidden)
        {
            view.StopAnimating();
        }
        else
        {
            view.StartAnimating();
        }
    }
}


来源:https://stackoverflow.com/questions/27906367/uiactivityindicatorview-startanimating-overriding-uiactivityindicatorview-hidden

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!