Hi,
currently i am working on hidding NavigationBar on iOS Platform. In Shared code I am creating CustomNavigationPage and then call it's Hide() method which implementation is described below.
In Shared code there is:
public class CustomNavigationPage : NavigationPage { public CustomNavigationPage(Page page) : base(page) { this.Title = page.Title; } public void Hide() { DependencyService.Get<INavigationBar>().Hide(); } }
Interface:
public interface INavigationBar { void Hide(); }
And native custom renderer:
[assembly: ExportRenderer(typeof(NavigationPage), typeof(CustomNavigationRenderer))] [assembly: Xamarin.Forms.Dependency(typeof(CustomNavigationRenderer))] namespace { public class CustomNavigationRenderer : NavigationRenderer, INavigationBar { public CustomNavigationRenderer() { } protected override void OnElementChanged(VisualElementChangedEventArgs e) { base.OnElementChanged(e); } public override void ViewDidLoad() { base.ViewDidLoad(); this.NavigationBar.TitleTextAttributes = new UIKit.UIStringAttributes { //The course title is Open Sans (15px ?, bold, black) Font = UIKit.UIFont.FromName("OpenSans-Bold", 15), }; Color getColor = Color.FromHex("#660044"); CoreGraphics.CGColor valueColor = new CoreGraphics.CGColor((nfloat)getColor.R, (nfloat)getColor.G, (nfloat)getColor.B, 255); this.NavigationBar.BackgroundColor = UIKit.UIColor.FromCGColor(valueColor); } public void Hide() { **TODO: None of those below worked in order to hide NavigationBar** **Does not work** //this.SetNavigationBarHidden(true, false); //this.NavigationBarHidden = true; **Does not work** //UIKit.UIApplication.SharedApplication.InputViewController.NavigationController.SetNavigationBarHidden(true, false); **Does not work** //ViewController.NavigationController.SetNavigationBarHidden(true, false); //NavigationController null **Does not work** //((UIKit.UINavigationController)ViewController).SetNavigationBarHidden(true, false); **Does not work** //var rootController = this.ViewController; //((UIKit.UINavigationController)rootController).SetNavigationBarHidden(true, false); **Does not work** //UIKit.UINavigationController controller = new UIKit.UINavigationController(); //controller.SetNavigationBarHidden(true, false); //this.AddChildViewController(controller); //this.ViewController.AddChildViewController(controller); } } }
It looks like NavigationController is null always, even if i am wrapping my ContentPage inside of CustomNavigationPage
ContentPage content = new ContentPage(); CustomNavigationPage course = new CustomNavigationPage(content);
Can someone point me to mistake that i am making or suggest solution for this. I would really appreciate some help.
Thanks.
On Android the action bar is part of the activity so as long as you can get access to the main activity instance you can hide or show the action bar. The activity is essentially global state so it doesn't matter that your renderer isn't associated with a valid element.
On iOS the navigation bar is part of the UINavigationController
, which is not a global thing (there could be more than one on the screen at a time), which means you must know which specific navigation controller you're trying to modify. There is no global state. That makes a singleton invalid for this use case.
The approach you took on Android may have worked for the reasons I gave above, but it's still not the right approach. It could have bad behavior. For instance, if a page that is not on screen tries to hide its navigation bar then your approach would hide the action bar, even though that page isn't even visible and should have no effect. It's a global state so you can't really tell.
Now that I'm thinking about this more I'm really confused why you don't just use this:
NavigationPage.SetHasNavigationBar(myPage, false);
That should work. Have you tried that?
You should be able to use SetHasNavigationBar without a custom renderer.
Answers
Dependency services are meant to be singletons, but renderers are not. What's happening is that you've registered this type of renderer class as a dependency service, and the first time you ask for an implementation of that service it will just construct a new one. That renderer it gives you is not associated with any actual
NavigationPage
so it can't really do anything useful.Instead of using a dependency service here you should just make a custom
NavigationPage
subclass and then make a custom renderer subclass for that. It would be something like this (warning: I only typed this, I didn't build it or test it):Hi Adam,
thank you for your fast answer.
I did what you said, but it looks like something is still missing since NavigationBar is still visible.
I am a little bit confused now because my first approach worked on Android Platform, even if i was using DependencyService combined with native NavigationRenderer (as described above).
On Android the action bar is part of the activity so as long as you can get access to the main activity instance you can hide or show the action bar. The activity is essentially global state so it doesn't matter that your renderer isn't associated with a valid element.
On iOS the navigation bar is part of the
UINavigationController
, which is not a global thing (there could be more than one on the screen at a time), which means you must know which specific navigation controller you're trying to modify. There is no global state. That makes a singleton invalid for this use case.The approach you took on Android may have worked for the reasons I gave above, but it's still not the right approach. It could have bad behavior. For instance, if a page that is not on screen tries to hide its navigation bar then your approach would hide the action bar, even though that page isn't even visible and should have no effect. It's a global state so you can't really tell.
Now that I'm thinking about this more I'm really confused why you don't just use this:
That should work. Have you tried that?
Your answer really explained a lot to me.
Well, if getting myPage as:
was right approach then NavigationPage.SetNavigationBarHidden(_page, false); did not work again
You should be able to use SetHasNavigationBar without a custom renderer.
Oh, you were right. It worked!
I used:
inside Shared Code, so now NavigationBar is completely hidden on iOS Platform. (it does not work on Android Platform)
Thank you!
Hi @adamkemp , forgive me. i am not able to get it running.
i always see the navigation bar on iOS.
My goal is this : i have to satisfy my boss' request. He wants the same appearance in android and ios.
I do like android behavior. i'd like to have in ios...the same masterdetail page as android :
On the first row the action bar and under it... all the contents.
I want to remove iOS action bar because i am not able to render it like android version.
So please can you tell me a way to put the same things on ios and android navigation bar ?
sorry for my english
@adamkemp . yesterday i updated my version of xf to 2.x because i needed to use ScrollView.ScrollX poperty.
Today i started a new project with master/detail page.
In older versions of xf , contentpage in ios cover the entire screen.
Today the application has a fixed toolbar like native android applications. Am i going crazy or is it a known behavior ?
Thanks in advance for your patience.
Alessandro