UIActivityIndicatorView.StartAnimating 覆盖 UIActivityIndicatorView.Hidden 绑定
UIActivityIndicatorView.StartAnimating overriding UIActivityIndicatorView.Hidden binding
我正在 Controller.ViewDidLoad
中创建一个 UIActivityIndicatorView
UIActivityIndicatorView spinner = new UIActivityIndicatorView();
spinner.StartAnimating();
spinner.Hidden = true;
this.Add(spinner);
然后我用MVVMCross绑定它
var set = this.CreateBindingSet<TipView, TipViewModel>();
set.Bind(spinner).For(v => v.Hidden).To(vm => vm.IsBusy).WithConversion("Inverse");
当视图最初加载时,UIActivityIndicatorView 正在旋转并且 可见。这是不正确的,因为 IsBusy 属性 在 ViewModel 的 Init() 中被明确设置为 False。我可以看到这种情况发生,我可以看到转换器反转值。
我知道绑定已正确连接,因为如果我触发更新 IsBusy 属性 的命令,指示器会按照我的预期显示和隐藏。只是初始状态不正确。
StartAnimating 方法似乎导致隐藏标志被覆盖。如果我不调用 StartAnimating,指示器将按预期隐藏和显示。当然这意味着我有一个非动画
指标。
我可以获得对 VM 的 WeakReference,监听 PropertyChanged 并调用 StartAnimating,但这似乎有点垃圾。
有没有人有更好的主意?
您可以做的一些选择:
订阅 PropertyChanged 更改并在事件处理程序中编写自定义代码(如您在问题中所建议的那样)
从UIActivityIndicatorView
继承并写一个publicget;set;
属性提供复合功能(调用Start
和Hidden
) 在 set
处理程序中
public class MyIndicatorView : UIActivityIndicatorView {
// ctors
private bool _superHidden;
public bool SuperHidden {
get { return _supperHidden; }
set { _superHidden = value; if (!value) StartAnimating() else StopAnimating(); Hidden = value; }
}
}
提供一个View
publicget;set;
属性并将复合功能放入其中(例如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; }
}
为 Hidden 编写自定义绑定,它替换默认功能并包含组合的 Start
和 Hidden
调用(有关自定义绑定的更多信息,有几个 N+ 1 个教程)
阅读@slodge 的回复后,我开始研究弱事件监听器和运行 在视图中隐藏和启动动画的代码。复制并粘贴该方法 3 次后,我意识到必须更改某些内容,因此我实施了他的第四条建议并编写了自定义绑定。 FWIW 这里是自定义绑定
/// <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();
}
}
}
我正在 Controller.ViewDidLoad
中创建一个 UIActivityIndicatorViewUIActivityIndicatorView spinner = new UIActivityIndicatorView();
spinner.StartAnimating();
spinner.Hidden = true;
this.Add(spinner);
然后我用MVVMCross绑定它
var set = this.CreateBindingSet<TipView, TipViewModel>();
set.Bind(spinner).For(v => v.Hidden).To(vm => vm.IsBusy).WithConversion("Inverse");
当视图最初加载时,UIActivityIndicatorView 正在旋转并且 可见。这是不正确的,因为 IsBusy 属性 在 ViewModel 的 Init() 中被明确设置为 False。我可以看到这种情况发生,我可以看到转换器反转值。
我知道绑定已正确连接,因为如果我触发更新 IsBusy 属性 的命令,指示器会按照我的预期显示和隐藏。只是初始状态不正确。
StartAnimating 方法似乎导致隐藏标志被覆盖。如果我不调用 StartAnimating,指示器将按预期隐藏和显示。当然这意味着我有一个非动画 指标。
我可以获得对 VM 的 WeakReference,监听 PropertyChanged 并调用 StartAnimating,但这似乎有点垃圾。
有没有人有更好的主意?
您可以做的一些选择:
订阅 PropertyChanged 更改并在事件处理程序中编写自定义代码(如您在问题中所建议的那样)
从
UIActivityIndicatorView
继承并写一个publicget;set;
属性提供复合功能(调用Start
和Hidden
) 在set
处理程序中public class MyIndicatorView : UIActivityIndicatorView { // ctors private bool _superHidden; public bool SuperHidden { get { return _supperHidden; } set { _superHidden = value; if (!value) StartAnimating() else StopAnimating(); Hidden = value; } } }
提供一个
View
publicget;set;
属性并将复合功能放入其中(例如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; } }
为 Hidden 编写自定义绑定,它替换默认功能并包含组合的
Start
和Hidden
调用(有关自定义绑定的更多信息,有几个 N+ 1 个教程)
阅读@slodge 的回复后,我开始研究弱事件监听器和运行 在视图中隐藏和启动动画的代码。复制并粘贴该方法 3 次后,我意识到必须更改某些内容,因此我实施了他的第四条建议并编写了自定义绑定。 FWIW 这里是自定义绑定
/// <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();
}
}
}