1

Here is my XAML. This ContentView is inside an AbsoluteLayout, but I don't think that matters. The idea is to have a frame with a list view, and a darkened background around it. I want to receive Overlay_Tapped if the user clicks outside of the Frame.

In iOS, SpinnerList_ItemSelected is never called. Instead, Overlay_Tapped is, even if the user clicks inside the Frame. In Android everything is fine.

I tried setting InputTransparent="False" in the Frame, ListView, and even the ViewCell. Does not help. What helps is setting NumberOfTapsRequired="2" in the TapGestureRecognizer, but that's obviously not my intention.

Help?

    <ContentView
        x:Name="SpinnerOverlay"
        AbsoluteLayout.LayoutBounds="0,0,1,1"
        AbsoluteLayout.LayoutFlags="All"
        IsVisible="False"
        InputTransparent="False"
        BackgroundColor="{x:Static engine:LmcColor.overlay}">

        <Frame
            OutlineColor="Black"
            Padding="10,10,10,10"
            BackgroundColor="White"
            HorizontalOptions="Center"
            VerticalOptions="Center">

            <ListView
                x:Name="SpinnerList"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                HasUnevenRows="False"
                SeparatorColor="Transparent"
                ItemSelected="SpinnerList_ItemSelected">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <ContentView
                                    Padding="5,0,5,0"
                                    BackgroundColor="{Binding BackColor}">
                                <Label
                                        Text="{Binding ItemText}"
                                        Style="{StaticResource StandardStyle}"/>
                            </ContentView>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Frame>

        <ContentView.GestureRecognizers>
            <TapGestureRecognizer Tapped="Overlay_Tapped"/>
        </ContentView.GestureRecognizers>
    </ContentView>
user1334767
  • 587
  • 4
  • 17
  • The answer on this link - https://stackoverflow.com/questions/37081987/how-do-i-overlay-two-xamarin-forms-layouts-such-that-both-can-receive-touch-inpu looks promising – Sharada Gururaj Aug 29 '17 at 03:35
  • I think there is a bug somewhere in there. I encountered this a few days ago. With a frame the InputTransparent property does not seem to do anything. – Gerald Versluis Aug 29 '17 at 06:10
  • It's not just the `Frame`... I tried putting the `Frame` into an extra `ContentView`, and vice versa, placing a `ContentView` inside the `Frame`. No bueno. I can prevent the outer view's Tap processor from being called, but at the "cost" of a call to the *inner* view's Tap processor. `ItemSelected` is not called no matter what. The link does look promising, I will certainly try that approach when I have time. – user1334767 Aug 31 '17 at 03:34

1 Answers1

1

The approach described in the link suggested by Sharada does not work for me. It was designed to make sure that in case of overlaying views, the "lower" one sometimes gets the tap. I need the opposite: my tap needs to go to the top view, but it gets caught and processed by the containing ContentView. Redefining HitTest() does not help here.

But trying this approach pushed me into custom renderers, and I was eventually able to solve my issue. I simply defined a "native" iOS gesture recognizer instead of a Xamarin.Forms one. I would've probably done this earlier if I was more familiar with native iOS development. I'm more of an Android guy.

Here is the custom renderer code. viewWithPoint.Frame.Size.Equals(Frame.Size) is how I determine that the click was not in a smaller subview. I'm using VisualElementRenderer<ContentView> instead of ViewRenderer as the latter does not properly process BackgroundColor.

using CoreGraphics;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(MobileCatalog.LmcContentView), typeof(MobileCatalog.iOS.LmcContentViewRenderer))]

namespace MobileCatalog.iOS
{
    /** Custom renderer in iOS is needed for the Overlay Content View:
     * otherwise the "overlay" tap recognizer block taps inside its content */

    public class LmcContentViewRenderer : VisualElementRenderer<ContentView>
    {
        public LmcContentViewRenderer()
        {
            UITapGestureRecognizer recognizer = new UITapGestureRecognizer((g) =>
            {
                if (Element != null)
                    LmcApp.W.ThePage.Overlay_Tapped(Element, null);
            });

            recognizer.ShouldBegin = ((g) =>
            {
                CGPoint point = g.LocationInView(this);
                UIView viewWithPoint = HitTest(point, null);
                return viewWithPoint != null && viewWithPoint.Frame.Size.Equals(Frame.Size);
            });

            AddGestureRecognizer(recognizer);
        }
    }
}
user1334767
  • 587
  • 4
  • 17