I have a Xamarin iOS app where I am presenting several modals one after the other.
Modal1 -> Modal2 -> Modal3 -> etc.
I would like to control and move all of their Frames. Is there any way that I can access all the modals that I have presented?
I tried something like:
var rootView = UIApplication.SharedApplication.KeyWindow.RootViewController.PresentedViewController;
foreach (UIView v in rootView)
{
//some code
}
Any tips would be very much appreciated. Thanks!
Try to use the following code :
public void GetAllMoadlViewController ()
{
UIViewController presentViewController = this.PresentingViewController;
UIViewController lastVC = this;
while(presentViewController!=null)
{
// One by one to get all ModalViewController that you present
var temp = presentViewController;
presentViewController = presentViewController.PresentingViewController;
lastVC = temp;
//do some thing you want
}
}
Related
I really want to implement a loading circle in the searchbar (overriding the cancel button while loading) on android like Netflix.
Currently Im getting the cancel button with
var searchBttId = searchView.Resources.GetIdentifier("android:id/search_close_btn", null, null);
var imbCancel = (searchView.FindViewById(searchBttId) as Android.Widget.ImageView)
but I dont know how to add a progresscircle and how to interact with it. I thought of adding it with AddView, but nothing pops up when testing.
Test:
var _v = new Android.Widget.ImageView(Context) { };
_v.SetBackgroundColor(new Android.Graphics.Color(200, 0, 0));
var c = new LayoutParams(500, 500);
_v.LayoutParameters = c;
searchView.AddView(_v);
I really want to implement a loading circle in the searchbar (overriding the cancel button while loading) on android like Netflix.
According to your description, you want to override searchbar's search_close_btn using other icon, like loading circle? Am I right?
If yes, I suggest you can use custom render to change searchbar's search_close_btn image.
Firstly, create custom Searchbar that inherit SearchBar in Form.cs.
public class MySearchBar:SearchBar
{
}
Then implementing custom render in Android platform.
[assembly: ExportRenderer(typeof(MySearchBar), typeof(MySearchBarRenderer))]
namespace demo3.Droid
{
public class MySearchBarRenderer: SearchBarRenderer
{
public MySearchBarRenderer(Context context):base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var searchView = Control;
int searchViewCloseButtonId = Control.Resources.GetIdentifier("android:id/search_close_btn", null, null);
var closeIcon = searchView.FindViewById(searchViewCloseButtonId);
(closeIcon as ImageView).SetImageResource(Resource.Drawable.plu3);
}
}
}
}
Please see the gif:
About Custom Render, you can take a look:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/entry
this may be a very basic question but I'd like to ask if how can I output the variable from a .droid to a .shared project in VS Xamarin. Let's say I have a variable with value in a renderer from a .droid, then I'd like to call it from a .shared page. How can I do this?
ScannerPageRenderer.cs (From .droid)
try
{
var BarcodeText = result.text.ToString();
var BarcodeType = result.typeText.ToString();
}
If you have a ScannerPageRenderer then this means that you are using a custom Renderer for a Xamarin.Forms page.
To pass back the values you can either create public properties in the Page, for this example let's call it ScannerPage then pass the values in the Element object of the custom renderer as this is the Page you are attaching the custom renderer.
ex
private void Scan()
{
//or however you are getting the result...
var result = getScanResult();
var scanPage = Element as ScannerPage;
if (scanPage == null)
{
return;
}
scanPage.BarcodeText = result.text.ToString();
scanPage.BarcodeType = result.typeText.ToString();
}
Another way would be that instead of the two properties you create a public method where you pass in the result object the same way as above with the properties. In such method you will then assign your properties or fields with the values.
private void Scan()
{
var result = getScanResult();
var scanPage = Element as ScannerPage;
if (scanPage == null)
{
return;
}
scanPage.OnScanCompleted(result);
}
Hope this helps.-
I'm just starting to learn programming with Xamarin and C# and I really struggle with this question.
I have a master detail app. In my master page I would like to easily add buttons that link to a detail page.
To do that I would like to make a method that I can call to with two parameters:
Button text and the detail page linked to the button.
so basicly:
public class MainLink : Button
{
public MainLink(string name,string page)
{
Text = name;
Command = new Command(o => {
App.MasterDetailPage.Detail = new NavigationPage(new page);
App.MasterDetailPage.IsPresented = false;
});
}
}
This is what I have so far. But the line:
App.MasterDetailPage.Detail = new NavigationPage(new page);
ofcourse doesn't work.
How can I convert a string to a page type when I know the page's name already?
You can also use the awsome Activator.CreateInstance() which takes in a Type which would be your page.
In order to get from a string to a Type you could do one of two things. Either try to get the type using the string alone (which I have not actually tried in Xamarin Forms) like so:
Type somethingPageType = Type.GetType("SomethingPage");
Or you could just pass the page Type into your Button's constructor and use that like so:
....
public MainLink(string name,string page, Type pageType) {
....
}
So to put it all together, either:
public class MainLink : Button
{
public MainLink(string name,string page)
{
Text = name;
Command = new Command(o => {
Type pageType = Type.GetType(page);
App.MasterDetailPage.Detail = new NavigationPage(Activator.CreateInstance(pageType) as Page);
App.MasterDetailPage.IsPresented = false;
});
}
}
Or:
public class MainLink : Button
{
public MainLink(string name, Type pageType)
{
Text = name;
Command = new Command(o => {
App.MasterDetailPage.Detail = new NavigationPage(Activator.CreateInstance(pageType) as Page);
App.MasterDetailPage.IsPresented = false;
});
}
}
Two things to keep in mind though. First would be that if your page takes parameters into the page's constructor, you will need to use an overload of Activator.CreateInstance() to make that work.
Also, if you use the Xamarin Linker to remove unused stuff, it will probably mess this method up so you need to specifically tell the linker to leave your reflection code alone, for iOS and for Android
We do not link all code, only SDK Assemblies so we do not need to worry about the linker issue but it looks like you can use the following code on iOS to fix it (though I have not actually tested the code below):
Create a .cs file in your iOS project named whatever (LinkerIncludes.cs for example) and add the following code:
public class LinkerIncludes {
public void Include() {
var x = new System.ComponentModel.ReferenceConverter (typeof(void));
}
}
NavigationPage gets an instance of a page.
You can use a switch statement to initialize a new page, according to the page string:
switch (page)
{
case "page1":
App.MasterDetailPage.Detail = new NavigationPage(new Page1);
break;
case "page2":
App.MasterDetailPage.Detail = new NavigationPage(new Page2);
break;
default:
App.MasterDetailPage.Detail = new NavigationPage(new DefaultPage);
break;
}
I followed the https://github.com/jamesmontemagno/Xam.NavDrawer example and Im able to successfully use the drawer layout with fragments(infact nested fragments with view pager). I have one issue, when I click the back button the navigation drawer menu item on the left side is not synced with the fragment that is shown.
This is how I navigate to other fragments
SupportFragmentManager.BeginTransaction().Replace(Resource.Id.content, fragment).AddToBackStack(fragment.Name).Commit();
I tried the OnAttachFragment method, but its not called on back stack. I also tried the SupportFragmentManager BackStackChanged method, but I could not get the current fragment that is in the view to update the navigation drawer menu title.
I had the same issue and couldn't find any solution as well. Although this question is kinda old, my solution may help someone. I'm not sure if it's the best solution but it works for me.
So first you need to add variable to store ID of previously checked item:
private int previousItemChecked;
set it initially to your default checked item:
if (savedInstanceState == null) {
selectItem(0);
previousItemChecked = 0;
}
then edit the fragment transaction so that the transaction title in backstack contains position of the previously checked item converted to string and after the transaction is done set the variable previousItemChecked to the currently checked item id:
fragmentManager.beginTransaction().replace(R.id.content_frame, selectedFragment).addToBackStack(""+previousItemChecked).commit();
previousItemChecked = mDrawerList.getCheckedItemPosition();
and finally in method onBackPressed you need to get the string previously assigned to fragment transaction, parse it to int and update the drawer according to the obtained id:
#Override
public void onBackPressed() {
if(fragmentManager.getBackStackEntryCount() > 0) {
String title = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount()-1).getName();
int pos = Integer.parseInt(title);
previousItemChecked = pos;
mDrawerList.setItemChecked(pos, true);
}
super.onBackPressed();
}
I took the code from my app created in Android Studio so it isn't for Xamarin but if you update the code it should work with it too. What's important here is the idea how it's done. I hope the answer is understandable and will help someone.
I had the same issue and I solved like this:
Inside selectItem we are passing the position Item;
So if position is 0 (or whatever is fragment we want it appears as first indipendently from it's position on the menu) we have to avoid to save the first transaction. So...
private void selectItem(position){
//...
if (position != 0)
{
SupportFragmentManager.BeginTransaction()
.Replace(Resource.Id.content_frame, fragment)
.AddToBackStack(fragment.Name)
.Commit();
}
else
{
SupportFragmentManager.BeginTransaction()
.Replace(Resource.Id.content_frame, fragment)
.Commit();
}
}
Managed to achieve this by injecting a DestinationChangedListener into the NavController like this:
NavController navController.addOnDestinationChangedListener(this);
and then:
#Override
public void onDestinationChanged(#NonNull NavController controller,
#NonNull NavDestination destination,
#Nullable Bundle arguments) {
NavigationView navigationView = findViewById(R.id.nav_view);
if(navigationView != null){
navigationView.setCheckedItem(destination.getId());
}
}
I'm trying to adapt my app for iOS 7. It's written with Xamarin and C#.
I'm having trouble with extra padding for the left button in the navigationbar.
I have a helper method for rendering my back-button which looks like this:
public static UIBarButtonItem GetBackButton (this UIViewController controller)
{
var backImage = new UIImage ("Images/back.png");
var backButton = new UIButton (UIButtonType.Custom);
backButton.Frame = new RectangleF (0, 0, 44, 44);
backButton.SetImage (backImage, UIControlState.Normal);
backButton.TouchUpInside += (object sender, EventArgs e) => {
var cancelBackNavigation = false;
if (controller is UIViewControllerBase) {
if (((UIViewControllerBase)controller).PrepareNavigateBack () != true) {
cancelBackNavigation = true;
}
}
if (cancelBackNavigation == false) {
controller.NavigationController.PopViewControllerAnimated (true);
}
};
return new UIBarButtonItem (backButton);
}
The navigationbar adds lots of padding before the back-button and making the image inside the back-button look very far away from its real position. The code above works fine in iOS 6.
I don't wanna use ContentEdgeInsets cause it will stretch the image and making it ugly.
Anyone with an idea of what to do?
I tried looking up your issue and found out that first, you need to hide the back button as follows:
NavigationItem.HidesBackButton = true;
Then for setting the button, you need to set it this way:
NavigationItem.BackBarButtonItem = yourButton;
That way, you won`t have the extra indentation.
Also you might find the following question useful:
Make a custom back button for UINavigationController
This is how I setup my NavigationBar
controller.View.BackgroundColor = Theme.BackgroundColor;
controller.NavigationItem.SetHidesBackButton (true, false);
controller.NavigationController.Toolbar.TintColor = Theme.BackgroundColor;
controller.NavigationController.NavigationBar.TintColor = Theme.BackgroundColor;
controller.NavigationController.SetNavigationBarHidden (show == false, false);
controller.NavigationController.NavigationBar.BackgroundColor = Theme.BackgroundColor;
controller.NavigationController.NavigationBar.SetTitleTextAttributes (Theme.NavigationBarTextAttributes);
controller.NavigationController.NavigationBar.Subviews [0].Alpha = 0.01f;