Xamarin Forms navigation bar with rounded central button - xamarin.forms

Is it possible to reproduce a bottom navigation bar like this in Xamarin Forms ?
Not with a Grid for example, but with a real navigation bar so this content stay static and navigation occurs in navigation area.

You can use custom renderer to achieve this in iOS:
In Xamarin.forms, create a TabbePage with 5 pages there:
<ContentPage Title="Tab 1" />
<ContentPage Title="Tab 2" />
<ContentPage Title="" />
<ContentPage Title="Tab 3" />
<ContentPage Title="Tab 4" />
In the TabbedRenderer, add the round button there:
[assembly : ExportRenderer(typeof(TabbedPage),typeof(MyRenderer))]
namespace App325.iOS
{
public class MyRenderer : TabbedRenderer
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
UIButton btn = new UIButton(frame: new CoreGraphics.CGRect(0, 0, 60, 60));
this.View.Add(btn);
//customize button
btn.ClipsToBounds = true;
btn.Layer.CornerRadius = 30;
btn.BackgroundColor = UIColor.Red;
btn.AdjustsImageWhenHighlighted = false;
//move button up
CGPoint center = this.TabBar.Center;
center.Y = center.Y - 20;
btn.Center = center;
//button click event
btn.TouchUpInside += (sender, ex) =>
{
//use mssage center to inkove method in Forms project
};
//disable jump into third page
this.ShouldSelectViewController += (UITabBarController tabBarController, UIViewController viewController) =>
{
if (viewController == tabBarController.ViewControllers[2])
{
return false;
}
return true;
};
}
}
}

Related

Fixed point button using absolute layout not responding to events

I have a listview on a content page. I have placed a fixed point button(think that is proper name) on the page using AvsoluteLayout. I have the button going to the top of the view using RaiseChild. The click event is not firing on the button but on the list view.
my on appearing where bubblebutton is the item at issue
protected override void OnAppearing()
{
base.OnAppearing();
IsBusy = true;
if(viewModel.PlayerActivities == null)
{
viewModel.LoadPlayerActivites.Execute(null);
}
IsBusy = false;
grid.RaiseChild(bubblebutton);
bubblebutton.Clicked += Bubblebutton_Clicked;
}
```
```
<AbsoluteLayout >
<ImageButton x:Name="bubblebutton"
BackgroundColor="Transparent"
Source="st_fab_button.png"
AbsoluteLayout.LayoutBounds="3,500,700,80"
Clicked="bubblebutton_Clicked"
IsEnabled="True"
/>
</AbsoluteLayout>
```
[button click is not happening][1]
Thank you for any help sorry for poor clip art skill
[1]: https://i.stack.imgur.com/QJixO.png
Welcome to SO !
If using Button inside Item of ListView , should use the MVVM architecture to do . That means we should use Binding model to get the click event .
Such as modified code as follow :
```
<AbsoluteLayout >
<ImageButton x:Name="bubblebutton"
BackgroundColor="Transparent"
Source="st_fab_button.png"
AbsoluteLayout.LayoutBounds="3,500,700,80"
Command="{Binding MyCommand}"
IsEnabled="True"
/>
</AbsoluteLayout>
```
Then in your ViewModle should declare the MyCommand :
public ICommand MyCommand { private set; get; }
public ViewModel()
{
MyCommand = new Command(
execute: () =>
{
// do some thing
RefreshCanExecutes();
},
canExecute: () =>
{
// return !IsEditing;
});
···
}
In addition , you also can pass Parameters , more info can have a look at this document .

How to make multiline text on tab items in iOS

I got 5 tabs on TabbedPage & last 2 tabs have long title name, on Android, it shows 3dots as ... when there is no more room space remaining for text.
eg.
tab 1 title - Title 1 for Tab1
tab 2 title - Title 2 for Tab2
tab 3 title - Title 3 for Tab3
Android - Title 1 f... | Title 2 f... | Title 3 f...
But on iOS it doesn't show 3dots, it shows complete text which can even override the title of another tab. Kind of text overlapping.
Basically I want my title of TabbedPage on multi-line, I use different content pages as tabs for my TabbedPage.
I can create MultiLine ContentPage n its working fine on its own. But when I set the MultiLine title content page as a tab for my TabbedPage, it only shows the first-line title.
Any solution for MultiLine TabbedPage Title on iOS like below
My Current renderer code
[assembly: ExportRenderer( typeof( TabbedPage ), typeof(ExtendedTabbedPageRenderer ) )]
namespace testBlu.iOS.Renderers
{
public class ExtendedTabbedPageRenderer : TabbedRenderer
{
public override void ViewDidAppear( bool animated )
{
base.ViewDidAppear( animated );
if( TabBar.Items != null )
{
UITabBarItem[] tabs = TabBar.Items;
foreach( UITabBarItem tab in tabs )
{
UITextAttributes selectedColor = new UITextAttributes { TextColor = UIColor.Black };
UITextAttributes fontSize = new UITextAttributes { Font = UIFont.SystemFontOfSize( 12 )};
tab.SetTitleTextAttributes( selectedColor, UIControlState.Normal );
tab.SetTitleTextAttributes( fontSize, UIControlState.Normal );
}
}
}
}
}
If need to show three dots the same with Android , here is a solution for you . Later if have solution for multi-lines will update here .
You can use Custom TabbedRenderer to implement it .
[assembly: ExportRenderer(typeof(MainPage), typeof(ExtendedTabbedPageRenderer))]
namespace AppTab3.iOS
{
public class ExtendedTabbedPageRenderer : TabbedRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
var tabs = Element as TabbedPage;
if(tabs != null)
{
for( int i = 0;i < TabBar.Items.Length;i++)
{
if (TabBar.Items[i] == null) return;
if(TabBar.Items[i].Title.Length > 6)
{
string showText = TabBar.Items[i].Title;
TabBar.Items[i].Title = showText.Substring(0, 5) + "...";
}
}
}
}
}
}
Here MainPage inside code is a TabbedPage :public partial class MainPage : TabbedPage
And here I set the limited length of TabBar Text is 6 . The Xaml is as follow :
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:views="clr-namespace:AppTab3.Views"
x:Class="AppTab3.Views.MainPage">
<TabbedPage.Children>
<NavigationPage Title="Browse">
<NavigationPage.Icon>
<OnPlatform x:TypeArguments="FileImageSource">
<On Platform="iOS" Value="tab_feed.png"/>
</OnPlatform>
</NavigationPage.Icon>
<x:Arguments>
<views:ItemsPage />
</x:Arguments>
</NavigationPage>
...
<NavigationPage Title="Page Five Long Title Page Five Long Title">
<NavigationPage.TitleView>
<Label Text="About Five Long Title" MaxLines="4"/>
</NavigationPage.TitleView>
<NavigationPage.Icon>
<OnPlatform x:TypeArguments="FileImageSource">
<On Platform="iOS"
Value="tab_about.png" />
</OnPlatform>
</NavigationPage.Icon>
<x:Arguments>
<views:AboutPage />
</x:Arguments>
</NavigationPage>
</TabbedPage.Children>
</TabbedPage>
The effect :
================================Update=============================
I have found the way to implement multiline title in tabbar item , need to modify code in TabbedRenderer as follow :
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
var tabs = Element as TabbedPage;
if (tabs != null)
{
for (int i = 0; i < TabBar.Items.Length; i++)
{
if (TabBar.Items[i] == null) continue;
if (TabBar.Items[i].Title.Length > 6)
{
string[] splitTitle = TabBar.Items[i].Title.Split(" ");
TabBar.Items[i].Title = splitTitle[0] + "\n" + splitTitle[1];
UITabBarItem item = TabBar.Items[i] as UITabBarItem;
UIView view = item.ValueForKey(new Foundation.NSString("view")) as UIView;
UILabel label = view.Subviews[1] as UILabel;
//label.Text = "Hello\nWorld!";
label.Lines = 2;
label.LineBreakMode = UILineBreakMode.WordWrap;
//var frame = label.Frame;
//label.Frame = CGRect.FromLTRB(frame.Location.X, frame.Location.Y, frame.Size.Width, frame.Size.Height + 20);
}
}
}
}
The effect:
Note : Althouh this way can implement it , however Apple not recommands to do this . It will affect the beauty of interface ,and make the frame of Tabbar item's shape distortion .
=============================Update with shared code=======================
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
var tabs = Element as TabbedPage;
if (tabs != null)
{
for (int i = 0; i < TabBar.Items.Length; i++)
{
if (TabBar.Items[i] == null) continue;
if (TabBar.Items[i].Title.Length > 6)
{
string[] splitTitle = TabBar.Items[i].Title.Split(" ");
if (null != splitTitle[1])
{
if (splitTitle[1].Length > 4)
{
string showText = splitTitle[1];
splitTitle[1] = showText.Substring(0, 3) + "...";
}
}
TabBar.Items[i].Title = splitTitle[0] + "\n" + splitTitle[1];
UITabBarItem item = TabBar.Items[i] as UITabBarItem;
UITextAttributes selectedColor = new UITextAttributes { TextColor = UIColor.Black };
UITextAttributes fontSize = new UITextAttributes { Font = UIFont.SystemFontOfSize(12) };
item.SetTitleTextAttributes(selectedColor, UIControlState.Selected);
item.SetTitleTextAttributes(fontSize, UIControlState.Selected);
UIView view = item.ValueForKey(new Foundation.NSString("view")) as UIView;
UILabel label = view.Subviews[1] as UILabel;
//label.Text = "Hello\nWorld!";
label.Lines = 2;
label.LineBreakMode = UILineBreakMode.WordWrap;
//var frame = label.Frame;
//label.Frame = CGRect.FromLTRB(frame.Location.X, frame.Location.Y, frame.Size.Width, frame.Size.Height + 10);
}
}
}
}
I think the most simple way (i.e, avoiding a custom renderer) would be to use a TitleView
Here's the official Microsoft sample.
https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/navigation-titleview/
Here's a blog post.
https://www.andrewhoefling.com/Blog/Post/xamarin-forms-title-view-a-powerful-navigation-view
In that TitleView you can use a Label and set the LineBreakMode property.

Xamarin Forms: Is it possible to view the pictures by right or left flip?

I have a photo gallery in my application. If I select a photo, the selected photo is shown on another page with right and left arrows. When tapping the left arrow the previous picture of the album is visible on the screen and if taps the right arrow the next picture in the album will come on the screen.
Screenshot
I need to view the next/previous pictures without arrow tapping. Is it possible to view the pictures by right or left swiping? Is there any controls for recognizing the right/left screen swiping?
CarouselView is here for you !!
https://devblogs.microsoft.com/xamarin/xamarin-forms-4-0-feature-preview-an-entirely-new-point-of-collectionview/
Technical note: Enable the CollectionView (which also enables the
CarouselView) with a feature flag just before you initialize
Xamarin.Forms in your MainActivity.cs and AppDelegate:
global::Xamarin.Forms.Forms.SetFlags("CollectionView_Experimental");
If you don't want to use the new feature you can add SwipeGestureRecognizer
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/swipe
For fast image loading and caching i use this library
https://github.com/luberda-molinet/FFImageLoading
Careful though because CarouselView is in preview, you shouldn't use it in production app.
So till CollectionView is not released you can use this one:
https://github.com/roubachof/Sharpnado.Presentation.Forms#carousel-layout
I need to view the next/previous pictures without arrow tapping. Is it possible to view the pictures by right or left swiping? Is there any controls for recognizing the right/left screen swiping?
If you want to do this, I suggest you can use CarouselViewControl. You need to install CarouseView.FormsPlugin by nuget. You can tap arrow or right or right swipping to view picture
then add this reference
<ContentPage
x:Class="Demo1.listviewcontrol.Page3"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv="clr-namespace:CarouselView.FormsPlugin.Abstractions;assembly=CarouselView.FormsPlugin.Abstractions">
<ContentPage.Content>
<Grid>
<cv:CarouselViewControl
x:Name="carousel"
AnimateTransition="True"
ItemsSource="{Binding MyItemsSource}"
Orientation="Horizontal"
PositionSelected="Handle_PositionSelected"
PositionSelectedCommand="{Binding MyCommand}"
Scrolled="Handle_Scrolled"
ShowArrows="true"
ShowIndicators="true" />
</Grid>
</ContentPage.Content>
public partial class Page3 : ContentPage
{
public Page3 ()
{
InitializeComponent ();
this.BindingContext = new MainViewModel();
}
private void Handle_PositionSelected(object sender, CarouselView.FormsPlugin.Abstractions.PositionSelectedEventArgs e)
{
Debug.WriteLine("Position " + e.NewValue + " selected.");
}
private void Handle_Scrolled(object sender, CarouselView.FormsPlugin.Abstractions.ScrolledEventArgs e)
{
Debug.WriteLine("Scrolled to " + e.NewValue + " percent.");
Debug.WriteLine("Direction = " + e.Direction);
}
}
public class MainViewModel
{
public ObservableCollection<View> _myItemsSource;
public ObservableCollection<View> MyItemsSource
{
set
{
_myItemsSource = value;
}
get
{
return _myItemsSource;
}
}
public Command MyCommand { protected set; get; }
public MainViewModel()
{
MyItemsSource = new ObservableCollection<View>()
{
new CachedImage() { Source = "a1.jpg", DownsampleToViewSize = true, Aspect = Aspect.AspectFill },
new CachedImage() { Source = "c2.jpg", DownsampleToViewSize = true, Aspect = Aspect.AspectFill },
new CachedImage() { Source = "c3.jpg", DownsampleToViewSize = true, Aspect = Aspect.AspectFill }
};
MyCommand = new Command(() =>
{
Debug.WriteLine("Position selected.");
});
}
}
I use CacheImage, so installing Xamarin.FFimageLoading.Forms by nuget.
Please add the following code in Mainactivity OnCreate method
CarouselViewRenderer.Init();
CachedImageRenderer.Init(true);

How to manage(hide) Back Button and Master Page in navigation while Deeplinking?

On HomePage of Button Update Profile, it redirects and working fine. But when I try to go to Update Profile page from any other place like DeepLink, it shows Back Button with a word Master Page. Can anybody please suggest me what I am missing here?
HomePage(Master)
<?xml version="1.0" encoding="UTF-8"?>
<local:MasterDetailPageWithLifecycle xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyProject;assembly=MyProject"
x:Class="MyProject.HomePage"
OnAppearingCommand="{Binding OnAppearingCommand}"
Title="Master Page">
<MasterDetailPage.Master>
<ContentPage Title="Home page" Icon="hamburger.png">
<ContentPage.Resources>
<ResourceDictionary>
<local:MenuItemDataTemplateSelector x:Key="menuItemDataTemplateSelector" HighlitedTemplate="{StaticResource highlitedTemplate}"
NormalTemplate="{StaticResource normalTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout BackgroundColor="{DynamicResource d8Purple}" VerticalOptions="FillAndExpand" Padding="0, 48, 0, 0">
<StackLayout Padding="0, 40, 0, 0" Spacing="0">
<ListView x:Name="listView" Margin="0,9,0,0" VerticalOptions="FillAndExpand" SeparatorVisibility="None"
ItemSelected="OnItemSelected" ItemTemplate="{StaticResource menuItemDataTemplateSelector}" />
</StackLayout>
</StackLayout>
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
</local:MasterDetailPageWithLifecycle>
HomePage.cs
public HomePage()
{
InitializeComponent();
BindingContext =_vm = App.Locator.Home;
NavigationPage.SetHasNavigationBar(this, false);
_masterPageItems = new List<MasterPageItem>();
_masterPageItems.Add(new MasterPageItem
{
Title = "Update Profile",
TargetType = nameof(EditProfilePage)
});
listView.ItemsSource = _masterPageItems;
}
public void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null)
{
var name = item.TargetType;
if (name == "EditProfilePage")
{
Detail = new MyProjectNavigationPage(new EditProfilePage());
listView.SelectedItem = null;
IsPresented = false;
}
}
}
public class MyProjectNavigationPage : NavigationPage
{
public MyProjectNavigationPage(Page root) : base(root)
{
if (Device.OS == TargetPlatform.iOS)
{
BarTextColor = Colors.d8Grey;
BarBackgroundColor = Color.White;
Title = root.Title;
}
}
}
EditProfile XAML
<?xml version="1.0" encoding="UTF-8"?>
<local:ContentPageWithCustomBackButton
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyProject;assembly=MyProject"
x:Class="MyProject.EditProfilePage"
OnAppearingCommand="{Binding OnAppearingCommand}"
Title="Update Profile">
<ContentPage.Content>
<Grid RowSpacing="0">
//Design content
</Grid>
</ContentPage.Content>
</local:ContentPageWithCustomBackButton>
EditProfile CS
public EditProfilePage()
{
InitializeComponent();
BindingContext=_editProfileViewModel = App.Locator.EditProfile;
_editProfileViewModel.PropertyChanged += ViewModel_PropertyChanged;
}
EditProfileDeeplink
public override void Navigate(string uri)
{
_navigationService.NavigateTo(nameof(EditProfilePage));
}
It's just because of when your trying to Navigate from HomePage(Masterpage) to EditProfile Page you set EditProfile Page as Master Detaill Page Like,
if (name == "EditProfilePage")
{
Detail = new MyProjectNavigationPage(new EditProfilePage());
listView.SelectedItem = null;
IsPresented = false;
}
but when you Come from other page you Only Navigate to that Page Like,
_navigationService.NavigateTo(nameof(EditProfilePage));
So you have to handle this Navigation by set page as MasterDetail(DetailPage) Like,
App.Current.MainPage = new MenuMaster {Detail = new NavigationPage(new EditProfile())};
With presenters :
Create class for your presenter
public class IosPagePresenter : MvxFormsIosViewPresenter
{
public override void Show(MvxViewModelRequest request)
{
if (request.PresentationValues?["NavigationCommand"] == "StackClear")
FormsApplication.MainPage = new ContentPage();
base.Show(request);
}
public IosPagePresenter(IUIApplicationDelegate applicationDelegate, UIWindow window, MvxFormsApplication formsApplication) : base(applicationDelegate, window, formsApplication)
{
}
}
Register this presenter in the setup.IOS
protected override IMvxIosViewPresenter CreatePresenter()
{
var presenter = new IosPagePresenter(ApplicationDelegate, Window, FormsApplication);
Mvx.RegisterSingleton<IMvxFormsViewPresenter>(presenter);
return presenter;
}
And call from ViewModel
var bundle = new MvxBundle(new Dictionary<string, string> { { "NavigationCommand", "StackClear" } });
await _navigationService.Navigate<SavedTankViewModel>(bundle);

Change hamburger icon in master detail navigation xamarin forms

I am working on Xamarin forms where I need to show master detail navigation after successful login screen. I want to change default hamburger icon but not able to change it.
Please see below code I am using.
Since my app have login screen so I don't want to show any navigation on Login screen. I am just setting main page in app.xaml.cs
public App()
{
InitializeComponent();
MainPage = new Login();
}
Now after login clicked I tried following approach to change icon but didn't work
var dashboard = new Dashboard(){Icon = "Menuicon.png" };
Application.Current.MainPage = dashboard;
Dashbaord is masterdetail page and on its ctor, I am setting detail page like below
Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(DashbaordDetail))) { Icon = "Menuicon.png" };
Its not reflecting new icon
You should use a custom renderer.
In your Android project, like this:
[assembly: ExportRenderer(typeof(CustomIcon.Views.MainPage), typeof(IconNavigationPageRenderer))]
namespace CustomIcon.Droid
{
public class IconNavigationPageRenderer : MasterDetailPageRenderer
{
private static Android.Support.V7.Widget.Toolbar GetToolbar() => (CrossCurrentActivity.Current?.Activity as MainActivity)?.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
var toolbar = GetToolbar();
if (toolbar != null)
{
for (var i = 0; i < toolbar.ChildCount; i++)
{
var imageButton = toolbar.GetChildAt(i) as ImageButton;
var drawerArrow = imageButton?.Drawable as DrawerArrowDrawable;
if (drawerArrow == null)
continue;
imageButton.SetImageDrawable(Forms.Context.GetDrawable(Resource.Drawable.newIcon));
}
}
}
}
}
In your iOS project only use the same icon from you xaml file in your PCL project, like this:
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:CustomIcon.Views;assembly=CustomIcon"
Title="MainPage"
Icon="newIcon.png"
x:Class="CustomIcon.Views.MainPage">
<MasterDetailPage.Master>
<local:MasterPage x:Name="masterPage" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<local:Page1 />
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
For more information see my repo on github: https://github.com/wilsonvargas/CustomIconNavigationPage
i applied this tweak and it helped me. now i can see back button also after navigation
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
if (toolbar != null)
{
for (var i = 0; i < toolbar.ChildCount; i++)
{
var imageButton = toolbar.GetChildAt(i) as ImageButton;
var drawerArrow = imageButton?.Drawable as DrawerArrowDrawable;
if (drawerArrow == null)
continue;
bool displayBack = false;
var app = Xamarin.Forms.Application.Current;
var detailPage = (app.MainPage as MasterDetailPage).Detail;
var navPageLevel = detailPage.Navigation.NavigationStack.Count;
if (navPageLevel > 1)
displayBack = true;
if (!displayBack)
ChangeIcon(imageButton, Resource.Drawable.iconMenu2);
if (displayBack)
ChangeIcon(imageButton, Resource.Drawable.back1);
}
}
}
private void ChangeIcon(ImageButton imageButton, int id)
{
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop)
imageButton.SetImageDrawable(Context.GetDrawable(id));
imageButton.SetImageResource(id);
}

Resources