If you attach a Release event to a button in Xamarin Forms you'll notice that it won't get fired when running on iOS if you press the button, maintain your press and move your finger off of the button and then release.
Released event would fire and change the button background color to Orange
Released event does NOT fire and button background color remains LightSeaGreen that was set by the Pressed event.
It looks like only Android fires the release event
I was browsing the iOS code and saw that the Released
event is only fired from OnButtonTouchUpInside
.
There should be a property in the event args that allows us to know whether the release occured inside or outside the button. That's important.
Hello, any news on this bugfix or there is some workaround?
Thanks
I agree with @SuperCorks , then dev can decide whether to ignore and not fire release event if release occurred outside of button bounds.
Hey all,
Altough @PureWeen had reported that the event DOES fire on android, I would like to report that I am using the latest nuget version of XamarinForms and the event does NOT fire on android.
steps to reproduce are the same as the OP of the issue suggested.
thanks in advance.
Edit:
A (not the best) workaround that I found for now is to have a boolean field that states if the button was not realeased properly, as following:
...
private bool WORKAROUND_BtnReleasedProblem;
...
private void Button_Released(object sender, EventArgs e)
{
WORKAROUND_BtnReleasedProblem = false;
ViewBackgroundColor = Color.Transparent;
WhiteBackground = !WhiteBackground;
SetColor();
}
private void Button_Pressed(object sender, EventArgs e)
{
if (WORKAROUND_BtnReleasedProblem)
Button_Released(sender, null);
ViewBackgroundColor = BorderColor;
WhiteBackground = !WhiteBackground;
SetColor();
WORKAROUND_BtnReleasedProblem = true;
}
thus when pressed again, the button would return to the previous state of 'released'
Hi, just to mention that as an iOS workaround, I wrote a platform effect that triggers the released event when the touch up occurs outside the button bounds.
using System;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ResolutionGroupName("Effects")]
[assembly: ExportEffect(typeof(EffectTest.iOS.Effects.SpecialButtonEffect), nameof(EffectTest.iOS.Effects.SpecialButtonEffect))]
namespace EffectTest.iOS.Effects
{
public class SpecialButtonEffect : PlatformEffect
{
protected override void OnAttached()
{
(Control as UIButton).TouchUpOutside += OnTouchUpOutside;
}
protected override void OnDetached()
{
(Control as UIButton).TouchUpOutside -= OnTouchUpOutside;
}
private void OnTouchUpOutside(object sender, EventArgs e)
{
(Element as IButtonController).SendReleased();
}
}
}
I experience this problem on Android. Forms 4.5.
Thanks for the suggestion @Peseur. I bumped into a similar issue with the ImageButton
on iOS, reported in #10859, then discovered that the same happens with regular Button
s on iOS too, so ended up here. Your solution inspired me to write my own, which will not just send the Released
-event, but will also fix reverting the visual state to normal (which was my original issue.
Same problem on Android (XF 3.6) : Released event not triggered when releasing the button outside.
With a LongPressBehavior :
` public class LongPressBehavior : Behavior
//timer to track long press
private Timer _timer;
//the timeout value for long press
private readonly int _duration;
//whether the button was released after press
private volatile bool _isReleased;
/// <summary>
/// Occurs when the associated button is long pressed.
/// </summary>
public event EventHandler LongPressed;
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command),
typeof(DelegateCommand<object>), typeof(LongPressBehavior), default);
public static readonly BindableProperty CommandParameterProperty =
BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(LongPressBehavior));
/// <summary>
/// Gets or sets the command parameter.
/// </summary>
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
/// <summary>
/// Gets or sets the command.
/// </summary>
public DelegateCommand<object> Command
{
get => (DelegateCommand<object>)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
protected override void OnAttachedTo(Button button)
{
base.OnAttachedTo(button);
this.BindingContext = button.BindingContext;
button.Pressed += Button_Pressed;
button.Released += Button_Released;
}
protected override void OnDetachingFrom(Button button)
{
base.OnDetachingFrom(button);
this.BindingContext = null;
button.Pressed -= Button_Pressed;
button.Released -= Button_Released;
}
/// <summary>
/// DeInitializes and disposes the timer.
/// </summary>
private void DeInitializeTimer()
{
lock (_syncObject)
{
if (_timer == null)
{
return;
}
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_timer.Dispose();
_timer = null;
Debug.WriteLine("Timer disposed...");
}
}
/// <summary>
/// Initializes the timer.
/// </summary>
private void InitializeTimer()
{
lock (_syncObject)
{
_timer = new Timer(Timer_Elapsed, null, _duration, Timeout.Infinite);
}
}
private void Button_Pressed(object sender, EventArgs e)
{
_isReleased = false;
InitializeTimer();
}
private void Button_Released(object sender, EventArgs e)
{
_isReleased = true;
DeInitializeTimer();
}
protected virtual void OnLongPressed()
{
var handler = LongPressed;
handler?.Invoke(this, EventArgs.Empty);
Console.WriteLine(Command is null);
if (Command != null && Command.CanExecute(CommandParameter))
{
Command.Execute(CommandParameter);
}
}
public LongPressBehavior()
{
_isReleased = true;
_duration = Duration;
}
public LongPressBehavior(int duration) : this()
{
_duration = duration;
}
private void Timer_Elapsed(object state)
{
DeInitializeTimer();
if (_isReleased)
{
return;
}
Device.BeginInvokeOnMainThread(OnLongPressed);
}
}`
Most helpful comment
There should be a property in the event args that allows us to know whether the release occured inside or outside the button. That's important.