Search code examples
xamarinxamarin.ioscustom-renderer

Xamarin iOS change Backgroundcolor after swipe over it


I have a Grid with Buttons and will change the background color if the user swipe over them. I created a custom Button class:

    public class TouchscreenTestButton : UIButton
{
    public override void TouchesBegan(NSSet touches, UIEvent evt)
    {
        base.TouchesBegan(touches, evt);
        this.BackgroundColor = UIColor.Green;

    }
    public override void TouchesMoved(NSSet touches, UIEvent evt)
    {
        base.TouchesMoved(touches, evt);
        UITouch touch = touches.AnyObject as UITouch;
        if (touch != null)
        {

            this.BackgroundColor = UIColor.Green;
            SetNeedsDisplay();
        }
    }
    public override void TouchesEnded(NSSet touches, UIEvent evt)
    {
        base.TouchesEnded(touches, evt);
        UITouch touch = touches.AnyObject as UITouch;
        if (touch != null)
        {
            this.BackgroundColor = UIColor.Green;
        }
    }
}

Also a Button Renderer for replace the normal UIButtons of iOS to my TouchscreenTestButton.

    public class TouchscreenTestButtonRenderer : ButtonRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
    {
        int columnIndex = Grid.GetColumn(e.NewElement);
        int rowIndex = Grid.GetRow(e.NewElement);
        TouchscreenTestButton button = new TouchscreenTestButton();
        button.TouchDown +=(sender, evt) => Control.BackgroundColor=UIColor.Green;

        base.SetNativeControl(button);

        base.OnElementChanged(e);
    }
}

That doesnt work, only the first button I touch will get a green backgroundcolor. After that I create a Renderer for the Grid:

    public class GridRenderer : ViewRenderer
{
    public override void TouchesMoved(NSSet touches, UIEvent evt)
    {
        base.TouchesMoved(touches, evt);
        var touch = touches.AnyObject as UITouch;
        for (int i = 0; i < touch.View.Subviews.Length; i++)
        {
            var test = touch.LocationInView(this);
            if (PointInside(touch.LocationInView(this), evt))
            {
                this.Subviews[i].TouchesBegan(touches, evt);
                this.Subviews[i].BackgroundColor = UIColor.Green;
                SetNeedsDisplay();
            }
        }
    }
}

But also this doesnt work. I see that the event is fired and the backgroundcolor of the subview will be changed but the view doesnt update.

Can please somebody help? Thanks a lot!


Solution

  • I have test with the following code on my side , it works.

    [assembly: ExportRenderer(typeof(Grid), typeof(GridRenderer))]
    namespace Slide.iOS
    {
    class GridRenderer :ViewRenderer
    {
    
        public override void TouchesBegan(NSSet touches, UIEvent evt)
        {
            base.TouchesBegan(touches, evt);
            var touch = touches.AnyObject as UITouch;
            for (int i = 0; i < touch.View.Subviews.Length; i++)
            {
                var test = touch.LocationInView(this);
                var buttonView = touch.View.Subviews[i];
    
                CGRect frame = buttonView.Frame;
    
                if (frame.Contains(test))
                { 
                    //pay attention
                    var button = buttonView.Subviews[0];
                    button.BackgroundColor = UIColor.Green;
                }
            }
        }
    
        public override void TouchesMoved(NSSet touches, UIEvent evt)
        {
            base.TouchesMoved(touches, evt);
            var touch = touches.AnyObject as UITouch;
            for (int i = 0; i < touch.View.Subviews.Length; i++)
            {
                var test = touch.LocationInView(this);
                var buttonView = touch.View.Subviews[i];
    
                CGRect frame = buttonView.Frame;
    
                if (frame.Contains(test)) {
                    //pay attention
                    var button = buttonView.Subviews[0];
                    button.BackgroundColor = UIColor.Green;
                }
            }
        }
    }
    

    }

    Result :

    enter image description here

    Details:

    1.Disable the button in Portable, since the click event will be conflict with the touch event of Grid.

    <Button BackgroundColor="Red" Grid.Row="0" Grid.Column="0" IsEnabled="False"/>
    

    2.Determine whether the point you moved to is in the frame of that subview(button).

    if (frame.Contains(test)) 
    

    3. Look at the buttonView, pay attention it was not the button!! I set the backgroundcolor on it at the beginning but it didn't work. When we layout control in a Grid, it will auto generate subviews according to its columns and rows,and then put the controls on the subviews.

    e.g

    <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
    
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
    

    The Grid above will have 4 subviews ,and four corresponding buttons on that subviews.