Reputation: 1445
I am currently trying to implement a custom made spinning loading wheel when I gather data. The problem that I am currently facing is that the wheel never starts spinning if I somewhere in the code makes the activityImage.IsVisible false.
What I am doing is setting the image as visible true once I enter the page and data is being gathered until the data has been gathered and i set it to false and then the while loop with the image should stop rotate.
But with my current code the image gets visible and goes invisible just like it should but the image is not rotating.
This is what I am working with:
Gather the data:
public async void GetData ()
{
var getData = await phpApi.getData(category);
activityImage.IsVisible = true;
loadLoadingWheel();
foreach (var items in getData["results"])
{
...gathering data
}
activityImage.IsVisible = false;
}
The image spinning function:
async Task loadLoadingWheel()
{
while (activityImage.isVisible)
{
await activityImage.RelRotateTo(500, 1000, Easing.SinOut);
}
}
So why is the wheel not spinning? If I remove activityImage.IsVisible = false;
completely the wheel rotates but obviously the wheel does not get removed/stops spinning once the data has been gathered.
Upvotes: 1
Views: 910
Reputation: 1102
From what it looks like, you're attempting to update the UI on a background thread. You can only update the UI on the main thread. I did something very similar to make my own animations happen. This is pretty janky and someone else may have a better solution. In fact, I'd put money on someone having a better solution. Something along these lines may work for you:
private CancellationTokenSource animateTimerCancellationTokenSource;
async void StartAnimationTimer(CancellationTokenSource tokenSource)
{
try
{
//maintain a reference to the token so we can cancel when needed
animateTimerCancellationTokenSource = tokenSource;
int idleTime = 1000; //ms
await Task.Delay(TimeSpan.FromMilliseconds(idleTime), tokenSource.Token);
//Do something here
Device.BeginInvokeOnMainThread(() =>
{
if (activityImage.isVisible)
{
activityImage.RelRotateTo(500, 1000, Easing.SinOut);
//Do this if you want to have it possibly happen again
StartAnimationTimer(new CancellationTokenSource());
}
});
}
catch (TaskCanceledException ex)
{
//if we cancel/reset, this catch block gets called
Debug.WriteLine(ex);
}
// if we reach here, this timer has stopped
}
To cancel your animation from happening again, you can run this.
if (animateTimerCancellationTokenSource != null)
{
animateTimerCancellationTokenSource.Cancel();
animateTimerCancellationTokenSource.Dispose();
animateTimerCancellationTokenSource = null;
}
The key here is this guy though: Device.BeginInvokeOnMainThread(() => { });
You need to do all your UI work on the main thread. This allows you to call stuff on the main thread from a background thread which is what your async
methods are working in.
So you would end up with this
public async void GetData ()
{
var getData = await phpApi.getData(category);
activityImage.IsVisible = true;
StartAnimationTimer(new CancellationTokenSource());
foreach (var items in getData["results"])
{
...gathering data
}
activityImage.IsVisible = false;
if (animateTimerCancellationTokenSource != null)
{
animateTimerCancellationTokenSource.Cancel();
animateTimerCancellationTokenSource.Dispose();
animateTimerCancellationTokenSource = null;
}
}
Upvotes: 2