Hey guys,
I am using Xamarin Forms to develop an App as part of my master thesis.
Now I got stuck with an iPhone 4 and iOS 7.1.
With iOS 8 and 8.4 @ iPhone 6 everything works like a charm.
Also on the Android device, but that's not relevant here.
This is the exception:
Foundation.MonoTouchException: Objective-C exception thrown. Name: NSInvalidArgumentException Reason: -[UIKit_UIScrollView__UIScrollViewDelegate tableView:heightForRowAtIndexPath:]: unrecognized selector sent to instance 0xb41ffa0
I figured out that the UIScrollView is my custom ListView which enables my XF PCL App to animate a header smoothly and provide some sort of parallax effect to effectively use the full screen for my list.
If I don't use my custom iOS Renderer for that list everything works just fine.
The underlying issue comes with these two lines c.Delegate = null;
and c.Scrolled += this.OnScroll;
As you can see I am setting the Delegate to null. The reason for this is, that c.Scrolled += this.OnScroll;
would lead to an exception:
System.InvalidOperationException: Event registration is overwriting existing delegate. Either just use events or your own delegate: UIKit.UITableViewDelegate UIKit.UIScrollView+_UIScrollViewDelegate
Sadly I have no clue what I could do to cope with this exception or figure out the right way to enable my ListViewRenderer to listen for OnScroll-Events.
Of course I tried to do something in this fashion c.Delegate = new UITableViewDelegate();
but this will also throw an exception.
The entire class:
using System;
using System.Linq;
using CoreGraphics;
using KinsmenApp.iOS.Renderer;
using KinsmenApp.Views.UserControls;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof (LazyLoadingListView), typeof (LazyLoadingListViewRenderer))]
namespace KinsmenApp.iOS.Renderer
{
public class LazyLoadingListViewRenderer : ListViewRenderer
{
private CoreGraphics.CGPoint _oldOffset = new CGPoint(0, 0);
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
var c = this.Control;
if (c != null)
{
c.Delegate = null;
c.Scrolled += this.OnScroll;
}
}
protected void OnScroll(object sender, EventArgs args)
{
var ctrl = this.Control;
var listView = this.Element as LazyLoadingListView;
if (listView == null || ctrl == null || !IsVisible())
return;
var offset = ctrl.ContentOffset;
var deltaX = Math.Max(_oldOffset.X, offset.X) - Math.Min(_oldOffset.X, offset.X);
var deltaY = Math.Max(_oldOffset.Y, offset.Y) - Math.Min(_oldOffset.Y, offset.Y);
var scrolledDown = _oldOffset.Y > offset.Y;
listView.FireScrolledEvent(new ScrollingEventArgs(offset.X, offset.Y, deltaX, deltaY, scrolledDown));
_oldOffset = offset;
}
private bool IsVisible()
{
var view = this.Control;
if (view == null)
return false;
var cells = view.VisibleCells;
return cells != null && cells.Any();
}
}
}
I'll try to write a custom delegate as it is adviced here: https://bugzilla.xamarin.com/show_bug.cgi?id=30020
But maybe someone already knows how to do it properly.
Thanks in advance for your help!