UIActivityIndicator is not working correctly Xamarin IOS - ios

I am including UIActivityIndicator in my UIViewController.
I need to bring a file from FTP using.
I have a button on my ViewController, upon clicking that button, download will start. I put a UIActivityIndicator (aiReceive) on my ViewController, That is appearing in stopped state on my view controller, but only animate upon completing file download.
I am making a iPad App in Xamarin IOS.
I added the Async Method, but i am getting the below error now.
The await operator can only be used when its containing lambda expression is marked with the async modifier
public partial class FirstViewController : UIViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
btnReceiveData.TouchUpInside += (object sender, EventArgs e) =>
{
//BTProgressHUD.Show("Receiving Data..."); --> This Component is also not working,
aiReceive.StartAnimating();
await GetFileAsync());
BTProgressHUD.Dismiss();
};
}
async Task GetFileAsync()
{
using (var client = new HttpClient())
{
try
{
aiReceive.StartAnimating(); /* --> Inimation is not starting at this point. Animation Start After Downloading file, download takes time, i want to show animation in the mean while.*/
using (WebClient ftpclient = new WebClient())
{
try
{
ftpclient.Credentials = new System.Net.NetworkCredential("UserName", "Password");
string sourceFilePath = #"ftp://ftp.google.com/FileName.pdf";
var FileDownloadStart = UIAlertController.Create("Info", "Data file received.", UIAlertControllerStyle.Alert);
FileDownloadStart.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, null));
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(FileDownloadStart, true, null);
//Downloading file from FTP.
ftpclient.DownloadFile(sourceFilePath, "C:\");
var FileDownloadAlert = UIAlertController.Create("Info", "Data file received.", UIAlertControllerStyle.Alert);
FileDownloadAlert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, null));
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(FileDownloadAlert, true, null);
}
catch (Exception ex)
{
File.Delete(EpazzDirectory + AuditorId.ToString());
var ExceptionAlert = UIAlertController.Create("Exception", ex.Message, UIAlertControllerStyle.Alert);
ExceptionAlert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, null));
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(ExceptionAlert, true, null);
}
}
}
}
}
}

You're blocking the UI thread, and that will prevent the changes you want to make to the UI from taking effect (in this case UIActivityIndicator spinning animation).
The best way to do this is using async/await like:
aiReceive.StartAnimating();
await YourFTPRequestAsync();
aiReceive.StopAnimating();
Here are official MSDN documentation on async\await and a sample driven hint on how it works

Related

UIAlert disappears when the page is change

I have to show an UI alert dialog in the Appdelegate and MainAcvitivy in Xamarin.Forms. For android, there is no problem but for ios it does not work properly.
First of all, if I use UIAlertView, it works without problem.
Task.Run(async () =>
{
await Device.InvokeOnMainThreadAsync(() =>
{
var alertView = new UIAlertView("Title", $"Message", null, "OK") { UserInteractionEnabled = true };
alertView.Clicked += (sender, args) =>
{
File.Delete(errorFilePath);
};
alertView.Show();
}).ConfigureAwait(false);
}).ConfigureAwait(false);
but unfortunately UIAlertView is deprecated. That's why I use UIViewController.
Nevertheless I can see the alert around 0.5 second in the splash screen but after that, I show my main page and the alert is gone.
Here is the code
Task.Run(async () =>
{
await Device.InvokeOnMainThreadAsync(() =>
{
UIAlertController alertView = UIAlertController.Create("Title", $"Message", UIAlertControllerStyle.Alert);
alertView.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, (action) => File.Delete(errorFilePath)));
TopViewController.PresentViewController(alertView, true, null);
//var appDelegate = (AppDelegate)UIApplication.SharedApplication.Delegate;
//appDelegate.Window.RootViewController.PresentViewController(alertView, true, null);
}).ConfigureAwait(false);
}).ConfigureAwait(false);
and here is my TopViewContoller
public static UIViewController TopViewController
{
get
{
UIApplication.SharedApplication.KeyWindow.WindowLevel = UIWindowLevel.Alert +1;
UIApplication.SharedApplication.KeyWindow.BackgroundColor = UIColor.Clear;
UIApplication.SharedApplication.KeyWindow.UpdateConstraints();
UIApplication.SharedApplication.KeyWindow.MakeKeyAndVisible();
UIApplication.SharedApplication.KeyWindow.RootViewController.UpdateViewConstraints();
return TopViewControllerWithRootViewController(UIApplication.SharedApplication.KeyWindow.RootViewController);
}
}
What does the code look like in method TopViewControllerWithRootViewController ?
Could you just replace TopViewController with UIApplication.SharedApplication.KeyWindow.RootViewController and try again ?
And why you place the code into a Task , it is unnecessary .
Remove the outer Task and just try the following code ,it works fine on my side .
UIAlertController alertView = UIAlertController.Create("Title", $"Message", UIAlertControllerStyle.Alert);
alertView.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, (action) => File.Delete(errorFilePath)));
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alertView, true, null);

Memory not released in PopUp xamarin forms android application

I am using a popup to show a image popup in xamarin forms android application. Each time when i open and close the popup the memory spikes and this is not recollected. I am using a constructor to open the popup. How can we properly dispose the memory inside the popup when its closed.
//call from the parent page
await Navigation.PushPopupAsync(new ImagePopUp(imgSrc));
//ImagePopUp
public ImagePopUp()
{
InitializeComponent();
var tapgesture = new TapGestureRecognizer();
tapgesture.Tapped += async (sender, e) => Handle_Clicked(sender, e);
this.Content.GestureRecognizers.Add(tapgesture);
}
public ImagePopUp(ImageSource src) : this()
{
try
{
PopUpImageHolder.HeightRequest = App.ScreenHeight * 0.8;
if (src != null)
{
PopUpImageHolder.Source = src;
}
}
catch (Exception ex)
{
Task.Run(async () => await DisplayAlert("Alert", ex.Message, "OK"));
}
and to close the popup i use
await PopupNavigation.PopAsync();
The image memory is spiking each time when i open the popup

Why is Xamarin.Auth throwing authentication error with OAuth1Authenticator and Twitter

I am currently using Xamarin.Auth on a iOS project to handle some user authentication via Facebook and Twitter in my application. The Facebook authentication using OAuth2Authenticator works great and my implementation was based mainly off the docs (http://components.xamarin.com/gettingstarted/xamarin.auth). Twitter however still uses OAuth1 it seems and thus I based my implementation mainly off the answer in this StackOverflow questions (https://stackoverflow.com/a/21982205). Everything works properly and I am able to retrieve user, tweets, etc. but after all the code executes I receive a "Authentication Error" popup on the screen saying "Object reference not set to an instance of an object." there is nothing printed to the console however as is the case with most normal errors I have seen thus far. I can dismiss the popup and everything continues to preform correctly. I believe I have narrowed the problem down to something within the OAuth1Authenticator request as I still receive the error when all of the other handling code has been commented out. Please reference the code below to see what might be the cause of this.
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
signupBtn.TouchUpInside += delegate {
LoginToTwitter(true, this);
};
}
void LoginToTwitter(bool allowCancel, UIViewController _vc)
{
var auth = new OAuth1Authenticator (
consumerKey: "My Consumer Key",
consumerSecret: "My Consumer Secret",
requestTokenUrl: new Uri("https://api.twitter.com/oauth/request_token"),
authorizeUrl: new Uri("https://api.twitter.com/oauth/authorize"),
accessTokenUrl: new Uri("https://api.twitter.com/oauth/access_token"),
callbackUrl: new Uri("My callback url"),
getUsernameAsync: (IDictionary<string, string> accountProperties) => {
string screen_name = "";
if (accountProperties.TryGetValue("screen_name", out screen_name)) {
Console.WriteLine("SN: {0}", screen_name);
Account a = new Account(screen_name, accountProperties);
AuthenticatorCompletedEventArgs e = new AuthenticatorCompletedEventArgs(a);
TwitterCompleted(e, _vc);
}
return null;}
);
auth.AllowCancel = allowCancel;
UIViewController authView = auth.GetUI ();
_vc.PresentViewController (authView, true, null);
}
void TwitterCompleted (AuthenticatorCompletedEventArgs e, UIViewController _vc)
{
var theAccount = e.Account;
var theProperties = theAccount.Properties;
foreach (var item in theProperties) {
Console.WriteLine (item); //debugging
}
InvokeOnMainThread (delegate {
_vc.DismissViewController (true, null);
});
AccountStore.Create ().Save (e.Account, "Twitter");
if (!e.IsAuthenticated) {
Console.WriteLine("Not authorized");
return;
}
theScreenName = e.Account.Properties["screen_name"];
theCount = "2";
IDictionary<string, string> theDict = new Dictionary<string, string>();;
theDict.Add("screen_name", theScreenName);
theDict.Add("count", theCount);
var request = new OAuth1Request("GET", new Uri("https://api.twitter.com/1.1/statuses/user_timeline.json"), theDict, e.Account, false);
request.GetResponseAsync().ContinueWith (t => {
if (t.IsFaulted)
Console.WriteLine("Error: {0}", t.Exception.InnerException.Message);
else if (t.IsCanceled)
Console.WriteLine("Canceled");
else
{
var obj = JsonValue.Parse (t.Result.GetResponseText());
Console.WriteLine("object: {0}", obj); // debugging
}
}, uiScheduler);
return;
}
private readonly TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
instead of returning null in "getUsernameAsync" return Task

MonoTouch, ZXing: Redirect to another ViewController after scan

I'm using the ZXing MobileScanner lib with my xamarin project for iOS (MonoTouch).
I set up the scanning, it is working fine, reading my QRCode and having the correct result.
After reading the QRCode I want to show another ViewController. I set a property in the second Controller with the result from scanning and then want to show the controller.
The second view is not shown on screen. No errors, no feedback, simply not shown.
I guess, the MobileScanner builds up its own View (which can be seen in the source of the lib) and adds this to the NavigationController - and that this causes my Controller to stay "behind". Testing with simply redirecting to my controller on button clicks is working fine.
I also tried to "dispose" the view by calling scanner.Cancel(), but this results in
Warning: Attempt to dismiss from view controller <UINavigationController: 0x19a0c60> while a presentation or dismiss is in progress!
Here is my code, any help on how to display my view is appreciated.
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
scanner = new MobileBarcodeScanner(this.NavigationController);
this.btnScan.TouchUpInside += (sender, e) => {
//Tell our scanner to use the default overlay
scanner.UseCustomOverlay = false;
//We can customize the top and bottom text of the default overlay
scanner.TopText = "Ticket vor den Scanner halten";
scanner.BottomText = "Code wird automatisch eingelesen";
//Start scanning
scanner.Scan ().ContinueWith((t) =>
{
//Our scanning finished callback
if (t.Status == System.Threading.Tasks.TaskStatus.RanToCompletion){
string msg = "";
ZXing.Result result = t.Result;
if (result != null && !string.IsNullOrEmpty (result.Text)) {
scanner.Cancel();
if(this.ticketScreen == null) {
this.ticketScreen = new TicketScreen();
}
this.ticketScreen.ticketUrl = result.Text;
this.NavigationController.PushViewController(this.ticketScreen, true);
} else {
msg = "Code nicht erkannt!";
this.InvokeOnMainThread(() => {
var av = new UIAlertView("Fehler!", msg, null, "OK", null);
av.Show();
});
}
}
});
};
}
I use this.InvokeOnMainThread(() and it works fine in my code.
The solution for you is:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
scanner = new MobileBarcodeScanner(this.NavigationController);
this.btnScan.TouchUpInside += (sender, e) => {
//Tell our scanner to use the default overlay
scanner.UseCustomOverlay = false;
//We can customize the top and bottom text of the default overlay
scanner.TopText = "Ticket vor den Scanner halten";
scanner.BottomText = "Code wird automatisch eingelesen";
//Start scanning
scanner.Scan ().ContinueWith((t) =>
{
//Our scanning finished callback
if (t.Status == System.Threading.Tasks.TaskStatus.RanToCompletion){
string msg = "";
ZXing.Result result = t.Result;
if (result != null && !string.IsNullOrEmpty (result.Text)) {
scanner.Cancel();
this.InvokeOnMainThread(() =>
{
if(this.ticketScreen == null) {
this.ticketScreen = new TicketScreen();
}
this.ticketScreen.ticketUrl = result.Text;
this.NavigationController.PushViewController(this.ticketScreen, true);
});
} else {
msg = "Code nicht erkannt!";
this.InvokeOnMainThread(() => {
var av = new UIAlertView("Fehler!", msg, null, "OK", null);
av.Show();
});
}
}
});
};
}

problems in silverlight 4 when using Action callbacks to check for successful file upload

So the async requirement for silverlight ends up in some really convoluted code!!
Im uploading a file just exactly like this answer suggests.
The difference is Im posting the file to an MVC action method. Everything works file except, like I commented on the bottom of that answer, I don't get any callback for when the file DOES NOT successfully upload.
So I created another action method in my mvc app (Services/CheckForFile/{id}) and it returns a string depending on whether the file is found.
Now, how and when do I call this mvc action method is the problem:
void DoUpload() { //Gets call on BtnUpload.Click
//opn is an OpenFileDialog
up.UploadFile(_filename, opn.File.OpenRead(),
e =>
{
//do some ui stuff here.
BeginCheck();// calling this causes PROBLEMS!
});
}
private void BeginCheck()
{
Uploader up = new Uploader();
up.CheckForFile(_filename, success =>
{
if (!success)
{
MessageBox.Show("There was problem uploading the file. Please try again", "Error", MessageBoxButton.OK);
}
});
}
Here is the problem:
When the BeginCheck() function runs, the file, for some reason, NEVER uploads! If I comment it out it does!? It seems like The BeginCheck() runs during the upload or something? Shouldn't it run after!?
How/where would I call BeginCheck() after the upload, to ensure the file has been uploaded?
Here is how I defined the Uploader class:
public class Uploader
{
public void UploadFile(string fileName, Stream data, Action<Exception> callback)
{
UriBuilder ub = new UriBuilder(_mvcurl+"Services/UploadFile/" + fileName);
WebClient c = new WebClient();
c.OpenWriteCompleted += (sender, e) =>
{
try
{
PushData(data, e.Result);
e.Result.Close();
data.Close(); //this does not block.
callback(null);//this ALWAYS hits!
}
catch (Exception err)
{
if (callback != null)
{
callback(err);
}
}
};
c.OpenWriteAsync(ub.Uri);
}
public void CheckForFile(string filename, Action<bool> callback)
{
UriBuilder ub = new UriBuilder(_mvcurl+"Services/CheckForFile/" + fileName);
WebClient c = new WebClient();
c.OpenReadCompleted += (sender, e) =>
{
using (StreamReader sw = new StreamReader(e.Result))
{
if (sw.ReadToEnd().Equals("Found", StringComparison.InvariantCultureIgnoreCase))
{
callback(true);
}
else
{
callback(false);
}
}
};
c.OpenReadAsync(ub.Uri);
}
private void PushData(Stream input, Stream output)
{//4KB is not a limitation. We only copy 4Kb at a time from in to out stream
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)
{
output.Write(buffer, 0, bytesRead);
}
}
}
I'm embarrased to say that the original answer of mine to which you refer isn't entirely accurate. It seems to work for what the OP wanted but in fact the code doesn't block at the point that I thought it did. In reality what you are actually looking for is the WriteStreamClosed event, its here that you can discover any failure of the request.
Here is an ammended version that works the way you are expecting:-
public void UploadFile(string fileName, Stream data, Action<Exception> callback)
{
UriBuilder ub = new UriBuilder(_mvcurl+"Services/UploadFile/" + fileName);
WebClient c = new WebClient();
c.OpenWriteCompleted += (sender, e) =>
{
try
{
PushData(data, e.Result);
e.Result.Close();
data.Close(); //this does not block.
}
catch (Exception err)
{
if (callback != null)
callback(err);
}
};
c.WriteStreamClosed += (sender, e) =>
{
if (callback != null)
callback(e.Error);
}
c.OpenWriteAsync(ub.Uri);
}
Now your BeginCheck will only run after the server has responded to the file upload.

Resources