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);
Related
I've taken a look at this code snippet:
https://developer.xamarin.com/recipes/ios/media/video_and_photos/choose_a_photo_from_the_gallery/ unfortunately this loads the gallery immediately and doesn't take into consideration giving the user an option to select first.
I'm trying to give the user two options:
1 - take a photo
2 - choose an existing photo from gallery
Both of these would be buttons you click on. Any ideas?
One way to do it is to display an ActionSheet with Prism.IPageDialogService to give choices to the user. You can even do something like this to have a cross-platform solution:
private async void AddImage() // This is your method for your button
{
string chosen = await _dialogService.DisplayActionSheetAsync("Which source do you want to use?", "Cancel", null, "Camera", "Galery");
if (chosen == "Camera")
{
TakePhoto();
}
else if (chosen == "Galery")
{
PickPhoto();
}
}
Implementation of TakePhoto():
private async void TakePhoto()
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await _dialogService.DisplayAlertAsync("Warning", "Camera not available", "OK");
return;
}
_mediaFile = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
SaveToAlbum = true,
Directory = "Sample",
Name = "sample" + DateTime.Now.ToLocalTime().ToString("yyyyMMddHHmmss") + ".jpg"
});
if (_mediaFile == null)
{
return;
}
ImageButtonAddGroup = _mediaFile.Path;
}
Implementation of PickPhoto():
private async void PickPhoto()
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await _dialogService.DisplayAlertAsync("Warning", "Camera not available", "OK");
return;
}
_mediaFile = await CrossMedia.Current.PickPhotoAsync();
if (_mediaFile == null)
{
return;
}
ImageButtonAddGroup = _mediaFile.Path;
}
If you want two buttons instead of one, you don't need the Prism.IPageDialogService and you can just bind the methods TakePhoto() and PickPhoto() to two different buttons.
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
I have a list which creates years and am using this list to dynamically create a UIAlertController.
The user clicks on a button from a row in a tableview, which launches the UIAlertController and the user can select a year.
My challenge is:
Each time I select a year from the controller,it selects the cancel button.
What is the correct way of getting the index of the selected action?
// Create a new Alert Controller
actionSheetAlert =
UIAlertController.Create ("", "SELECT YEAR", UIAlertControllerStyle.ActionSheet);
try {
for (int i = 0; i < years.Count; i++) {
// Add Actions
actionSheetAlert.AddAction (
UIAlertAction.Create (years [i].ToString (), UIAlertActionStyle.Default,
(action) => {
string year = actionSheetAlert.Actions.ElementAt(i).ToString();
Console.WriteLine ("Selected year:" + year);
this.DismissViewController (true, null);
}
));
}
} catch (Exception ex) {
Console.WriteLine (ex.Message + ex.StackTrace);
}
actionSheetAlert.AddAction (
UIAlertAction.Create ("Cancel", UIAlertActionStyle.Cancel,
(action) => {
this.DismissViewController (true, null);
}
));
this.PresentViewController (actionSheetAlert, true, null);
The solution was quite simple.
Simply access the Title property of the action selected like this:
Change this
string year = actionSheetAlert.Actions.ElementAt(i).ToString();
To this
string year = action.Title;
I have created a very basic ios project that has a single button on it with it's title text property set to "Start". I am trying to have this button count down from 15.
I have added the event handler for the button as below.
void StartButton_TouchUpInside (object sender, EventArgs ea) {
_currentCount = 15;
_timer = NSTimer.CreateRepeatingScheduledTimer(TimeSpan.FromSeconds(1), () =>
{
if (_currentCount <= 0) {
StartButton.TitleLabel.Text = "Start";
_timer.Dispose();
} else {
string titleText = (_currentCount--).ToString ();
//Console.WriteLine(titleText);
StartButton.TitleLabel.Text = titleText;
//Console.WriteLine(StartButton.TitleLabel.Text);
//StartButton.SetNeedsDisplay();
}
});
}
So here is the strange thing. The Button changes to 15 just fine then it flashes back to "Start" then 14. Then 13 then "Start" then 12 etc... etc... etc...
Why is it doing this? And how do I prevent it from flashing back to start. Thanks in advance.
Try to use this.InvokeOnMainThread
private int counter = 0;
public override void WillActivate()
{
Console.WriteLine ("{0} will activate", this);
_timer = NSTimer.CreateRepeatingScheduledTimer(1, (timer) =>
{
this.InvokeOnMainThread(()=>{
Console.WriteLine("++++++++ NSTimer Call");
this.AlertTimeLabel.SetText(counter.ToString());
counter++;
});
});
}
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();
});
}
}
});
};
}