自定义“套用刷新”在iOS上不起作用

我正在遵循James Montemagno的教程为版式添加“拉动刷新”支持,它在Android上完美运行,但是当我导航到与Android相同的页面时,iOS会生成以下错误。

System.InvalidCastException:<超时超出了获取异常详细信息>

我要显示的页面是一个简单的StackLayout,它在Android上也能完美运行。

这是本教程中的我的iOS渲染器类

[assembly: ExportRenderer(typeof(RefreshableLayout), typeof(RefreshableLayoutiOS))]
namespace SocialNetwork.iOS.Renderers
{
[Preserve(AllMembers = true)]
public class RefreshableLayoutiOS : ViewRenderer<RefreshableLayout, UIView>
{
    public async static void Init()
    {
        var temp = DateTime.Now;
    }

    UIRefreshControl refreshControl;

    protected override void OnElementChanged(ElementChangedEventArgs<RefreshableLayout> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement != null || Element == null)
            return;

        refreshControl = new UIRefreshControl();

        refreshControl.ValueChanged += OnRefresh;

        try
        {
            TryInsertRefresh(this);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("View is not supported in PullToRefreshLayout: " + ex);
        }

        UpdateColors();
        UpdateIsRefreshing();
        UpdateIsSwipeToRefreshEnabled();
    }

    bool set;
    nfloat origininalY;

    bool TryOffsetRefresh(UIView view, bool refreshing, int index = 0)
    {
        if (view is UITableView)
        {
            var uiTableView = view as UITableView;
            if (!set)
            {
                origininalY = uiTableView.ContentOffset.Y;
                set = true;
            }

            if (refreshing)
                uiTableView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY - refreshControl.Frame.Size.Height), true);
            else
                uiTableView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY), true);
            return true;
        }

        if (view is UICollectionView)
        {

            var uiCollectionView = view as UICollectionView;
            if (!set)
            {
                origininalY = uiCollectionView.ContentOffset.Y;
                set = true;
            }
            if (refreshing)
                uiCollectionView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY - refreshControl.Frame.Size.Height), true);
            else
                uiCollectionView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY), true);
            return true;
        }


        if (view is UIWebView)
        {
            //can't do anything
            return true;
        }


        if (view is UIScrollView)
        {
            var uiScrollView = view as UIScrollView;

            if (!set)
            {
                origininalY = uiScrollView.ContentOffset.Y;
                set = true;
            }
            if (refreshing)
                uiScrollView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY - refreshControl.Frame.Size.Height), true);
            else
                uiScrollView.SetContentOffset(new CoreGraphics.CGPoint(0, origininalY), true);
            return true;
        }

        if (view.Subviews == null)
            return false;

        for (int i = 0; i < view.Subviews.Length; i++)
        {
            var control = view.Subviews[i];
            if (TryOffsetRefresh(control, refreshing, i))
                return true;
        }

        return false;
    }


    bool TryInsertRefresh(UIView view, int index = 0)
    {


        if (view is UITableView)
        {
            var uiTableView = view as UITableView;
            uiTableView = view as UITableView;
            view.InsertSubview(refreshControl, index);
            return true;
        }


        if (view is UICollectionView)
        {
            var uiCollectionView = view as UICollectionView;
            uiCollectionView = view as UICollectionView;
            view.InsertSubview(refreshControl, index);
            return true;
        }


        if (view is UIWebView)
        {
            var uiWebView = view as UIWebView;
            uiWebView.ScrollView.InsertSubview(refreshControl, index);
            return true;
        }

        if (view is UIScrollView)
        {
            var uiScrollView = view as UIScrollView;
            view.InsertSubview(refreshControl, index);
            uiScrollView.AlwaysBounceVertical = true;
            return true;
        }

        if (view.Subviews == null)
            return false;

        for (int i = 0; i < view.Subviews.Length; i++)
        {
            var control = view.Subviews[i];
            if (TryInsertRefresh(control, i))
                return true;
        }

        return false;
    }

    BindableProperty rendererProperty;

    BindableProperty RendererProperty
    {
        get
        {
            if (rendererProperty != null)
                return rendererProperty;

            var type = Type.GetType("Xamarin.Forms.Platform.iOS.Platform, Xamarin.Forms.Platform.iOS");
            var prop = type.GetField("RendererProperty");
            var val = prop.GetValue(null);
            rendererProperty = val as BindableProperty;

            return rendererProperty;
        }
    }

    void UpdateColors()
    {
        if (RefreshView == null)
            return;
        if (RefreshView.RefreshColor != Color.Default)
            refreshControl.TintColor = RefreshView.RefreshColor.ToUIColor();
        if (RefreshView.RefreshBackgroundColor != Color.Default)
            refreshControl.BackgroundColor = RefreshView.RefreshBackgroundColor.ToUIColor();
    }


    void UpdateIsRefreshing()
    {
        IsRefreshing = RefreshView.IsRefreshing;
    }

    void UpdateIsSwipeToRefreshEnabled()
    {
        refreshControl.Enabled = RefreshView.IsPullToRefreshEnabled;
    }

    public RefreshableLayout RefreshView
    {
        get { return Element; }
    }



    bool isRefreshing;

    public bool IsRefreshing
    {
        get { return isRefreshing; }
        set
        {
            bool changed = IsRefreshing != value;

            isRefreshing = value;
            if (isRefreshing)
                refreshControl.BeginRefreshing();
            else
                refreshControl.EndRefreshing();

            if (changed)
                TryOffsetRefresh(this, IsRefreshing);
        }
    }

    void OnRefresh(object sender, EventArgs e)
    {
        if (RefreshView?.RefreshCommand?.CanExecute(RefreshView?.RefreshCommandParameter) ?? false)
        {
            RefreshView.RefreshCommand.Execute(RefreshView?.RefreshCommandParameter);
        }
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        if (e.PropertyName == RefreshableLayout.IsPullToRefreshEnabledProperty.PropertyName)
            UpdateIsSwipeToRefreshEnabled();
        else if (e.PropertyName == RefreshableLayout.IsRefreshingProperty.PropertyName)
            UpdateIsRefreshing();
        else if (e.PropertyName == RefreshableLayout.RefreshColorProperty.PropertyName)
            UpdateColors();
        else if (e.PropertyName == RefreshableLayout.RefreshBackgroundColorProperty.PropertyName)
            UpdateColors();
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (refreshControl != null)
        {
            refreshControl.ValueChanged -= OnRefresh;
        }
    }
}
}

我获得了本教程GitHub的代码

编辑:

XAML

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="SocialNetwork.TestScrollPage" xmlns:local="clr-namespace:SocialNetwork.Renderers">
<ContentPage.Content>
    <StackLayout>
        <local:RefreshableLayout x:Name="RefreshableLayout" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
            <StackLayout>

            </StackLayout>
        </local:RefreshableLayout>
    </StackLayout>
</ContentPage.Content>

CS:

public partial class TestScrollPage : ContentPage
{
    public TestScrollPage ()
    {
        InitializeComponent ();
        RefreshableLayout.RefreshCommand = new Command(() => RefreshPage());
    }

    public void RefreshPage()
    {
        RefreshableLayout.IsRefreshing = false;
        DisplayAlert("ok", "ok", "ok");
    }
}

我使用导航到页面 Detail = new TestScrollPage();

编辑2:

    public partial class RefreshableLayout : ContentView
{
    public static readonly BindableProperty IsRefreshingProperty =
        BindableProperty.Create(nameof(IsRefreshing), typeof(bool), typeof(RefreshableLayout), false);

    /// <summary>
    /// Gets or sets a value indicating whether this instance is refreshing.
    /// </summary>
    /// <value><c>true</c> if this instance is refreshing; otherwise, <c>false</c>.</value>
    public bool IsRefreshing
    {
        get { return (bool)GetValue(IsRefreshingProperty); }
        set
        {
            if ((bool)GetValue(IsRefreshingProperty) == value)
                OnPropertyChanged(nameof(IsRefreshing));

            SetValue(IsRefreshingProperty, value);
        }
    }

    /// <summary>
    /// The is pull to refresh enabled property.
    /// </summary>
    public static readonly BindableProperty IsPullToRefreshEnabledProperty =
        BindableProperty.Create(nameof(IsPullToRefreshEnabled), typeof(bool), typeof(RefreshableLayout), true);

    /// <summary>
    /// Gets or sets a value indicating whether this instance is pull to refresh enabled.
    /// </summary>
    /// <value><c>true</c> if this instance is pull to refresh enabled; otherwise, <c>false</c>.</value>
    public bool IsPullToRefreshEnabled
    {
        get { return (bool)GetValue(IsPullToRefreshEnabledProperty); }
        set { SetValue(IsPullToRefreshEnabledProperty, value); }
    }


    /// <summary>
    /// The refresh command property.
    /// </summary>
    public static readonly BindableProperty RefreshCommandProperty =
        BindableProperty.Create(nameof(RefreshCommand), typeof(ICommand), typeof(RefreshableLayout));

    /// <summary>
    /// Gets or sets the refresh command.
    /// </summary>
    /// <value>The refresh command.</value>
    public ICommand RefreshCommand
    {
        get { return (ICommand)GetValue(RefreshCommandProperty); }
        set { SetValue(RefreshCommandProperty, value); }
    }

    /// <summary>
    /// Gets the Refresh command 
    /// </summary>
    public static readonly BindableProperty RefreshCommandParameterProperty =
        BindableProperty.Create(nameof(RefreshCommandParameter),
            typeof(object),
            typeof(RefreshableLayout),
            null,
            propertyChanged: (bindable, oldvalue, newvalue) => ((RefreshableLayout)bindable).RefreshCommandCanExecuteChanged(bindable, EventArgs.Empty));

    /// <summary>
    /// Gets or sets the Refresh command parameter
    /// </summary>
    public object RefreshCommandParameter
    {
        get { return GetValue(RefreshCommandParameterProperty); }
        set { SetValue(RefreshCommandParameterProperty, value); }
    }

    /// <summary>
    /// Executes if enabled or not based on can execute changed
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="eventArgs"></param>
    void RefreshCommandCanExecuteChanged(object sender, EventArgs eventArgs)
    {
        ICommand cmd = RefreshCommand;
        if (cmd != null)
            IsEnabled = cmd.CanExecute(RefreshCommandParameter);
    }

    /// <summary>
    /// Color property of refresh spinner color 
    /// </summary>
    public static readonly BindableProperty RefreshColorProperty =
        BindableProperty.Create(nameof(RefreshColor), typeof(Color), typeof(RefreshableLayout), Color.Default);

    /// <summary>
    /// Refresh  color
    /// </summary>
    public Color RefreshColor
    {
        get { return (Color)GetValue(RefreshColorProperty); }
        set { SetValue(RefreshColorProperty, value); }
    }



    /// <summary>
    /// Color property of refresh background color
    /// </summary>
    public static readonly BindableProperty RefreshBackgroundColorProperty =
        BindableProperty.Create(nameof(RefreshBackgroundColor), typeof(Color), typeof(RefreshableLayout), Color.Default);

    /// <summary>
    /// Refresh background color
    /// </summary>
    public Color RefreshBackgroundColor
    {
        get { return (Color)GetValue(RefreshBackgroundColorProperty); }
        set { SetValue(RefreshBackgroundColorProperty, value); }
    }


    /// <param name="widthConstraint">The available width for the element to use.</param>
    /// <param name="heightConstraint">The available height for the element to use.</param>
    /// <summary>
    /// Optimization as we can get the size here of our content all in DIP
    /// </summary>
    protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
    {
        if (Content == null)
            return new SizeRequest(new Size(100, 100));

        return base.OnMeasure(widthConstraint, heightConstraint);
    }
}
陆路-MSFT

请阅读有关Xamarin Liver Player的文档它声明了限制:

  • Xamarin表单不支持自定义渲染器。

使用Xamarin Liver Player时,还存在其他一些限制或问题。因此,我建议您使用模拟器或真实的物理设备来测试您的项目。

如果您没有Mac。您也可以尝试下载Enterprise Visual Studio,以使模拟器映射到Windows。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在后台模式下,FCM自定义声音在iOS上不起作用

当动态链接具有自定义子域时,Firebase动态链接在iOS上不起作用

iOS中的自定义字体不起作用

自定义字体在iOS 8中不起作用

自定义jQuery在Wordpress Ninja Forms插件上不起作用

自定义委托在iOS中不起作用

自定义进度可绘制在Android Lollipop(API 21)设备上不起作用

Yii2:自定义设置方法/数据转换在ActiveRecord上不起作用?

为什么排序方法在此自定义AbstractList实现上不起作用?

自定义中间件Laravel在api路由上不起作用

自定义字体在移动设备上不起作用

尾部截断在自定义Xamarin.iOS表格视图单元上不起作用

自定义Cursor CSS在网页的某些部分上不起作用

CSS中指定的自定义字体在Android 4.4上不起作用

UITableView自定义单元在iPad Storyboard上不起作用,但在iPhone上起作用

Java自定义光标在新计算机上不起作用

自定义UIButton上的自动布局在某些设备上不起作用

Wordpress音频标签在自定义字段上不起作用

wp-pagenavi在自定义查询wordpress上不起作用

ng-class在使用templateURL的自定义指令上不起作用

自定义Google Maps样式在iPad上不起作用

MVVM Cross ShowViewModel在自定义的UWP上不起作用

text-align:center在自定义元素上不起作用

对于自定义Helper类,Blade @inject()在Laravel 5.2上不起作用

DateTime.Parse在MVC自定义绑定程序上不起作用

CloudFlare“ SSL:灵活” HTTPS在自定义端口上不起作用

iOS 按钮在自定义视图中不起作用

链接在 Facebook 页面自定义选项卡上不起作用

为什么“内容安全策略”的自定义标头在 github 上不起作用?