Get native iOS system tab bar icons for TabbedPage on Xamarin.Forms - xaml

I am new to Xamarin.Forms and XAML. I am trying to get tab icons to display for ONLY IOS for the my different pages in my TabbedPage. I did a bit of search and I have come to know that UIKit has a reference to system icons available on IOS at UITabBarSystem. How can I make use of the elements in that enum without having to get those icons from the internet? The TabbedPage is the root with children pages that are ContentPages and ListView pages. So as you will see from the attached sample, I would like the "IconImageSource" property for FavouritesPage to get the image from the iOS UIKit. Is that possible?
<?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:views="clr-namespace:PhoneApp.Views"
x:Class="PhoneApp.Views.MainPage">
<TabbedPage.Title>
</TabbedPage.Title>
<TabbedPage.Children>
<views:FavouritesPage Title="Favourites" IconImageSource=""/>
<views:RecentsPage Title="Recents"/>
<views:ContactsPage Title="Contacts"/>
<views:KeypadPage Title="Keypad"/>
<views:VoicemailPage Title="Voicemail"/>
</TabbedPage.Children>
</TabbedPage>

I think I found the right solution for you. If you want to use native Api's on Xamarin controls, you can use custom renderer for them which are great! Here is the renderer for the TabedPage:
[assembly: ExportRenderer(typeof(MainPage), typeof(MyTabbedPageRenderer))]
namespace TestApp.iOS
{
public class MyTabbedPageRenderer : TabbedRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (TabBar?.Items == null) return;
//Setting the Icons
TabBar.Items[0].Image = GetTabIcon(UITabBarSystemItem.Search);
TabBar.Items[1].Image = GetTabIcon(UITabBarSystemItem.Downloads);
TabBar.Items[2].Image = GetTabIcon(UITabBarSystemItem.Bookmarks);
}
private UIImage GetTabIcon(UITabBarSystemItem systemItem)
{
//Convert UITabBarItem to UIImage
UITabBarItem item = new UITabBarItem(systemItem, 0);
return UIImage.FromImage(item.SelectedImage.CGImage, item.SelectedImage.CurrentScale, item.SelectedImage.Orientation);
}
}
}
I created a sample for you, which you can find here. If you have any questions, please ask!
Regards

Related

howTo get Image Resource of a ImageButton in kotlin

i want change the ImageResource of a ImageButton that i have find by id.
Motivation
a ImageButton(bottom) works as a reminder/backup of the last click of a ImageButton(top) .
setup:
some ImageButton (at the top of the app).
a ImageButton (at the bottom of the app).
example without errors, but don't find ImageResource of idR1
findViewById<ImageButton>(idR1).setOnClickListener {
findViewById<ImageButton>(idR5_oppCiv).setImageResource(R.drawable.athen_cavalry_swordsman);
not working examples
findViewById<ImageButton>(idR1).setOnClickListener {
findViewById<ImageButton>(idR5_oppCiv).setImageResource(it.resources.getDrawable());
findViewById<ImageButton>(idR1).setOnClickListener {
findViewById<ImageButton>(idR5_oppCiv).setImageResource(it.getImageResource());
try to get and set Drawable
following causes the app to crash when i click on an ImageButton.
Here i use a defType "res" to get the resource (the image hopefully).
val resR1: Int = resources.getIdentifier("r1col$i", "res", this.packageName)
findViewById<ImageButton>(idR1).setOnClickListener {
findViewById<ImageButton>(idR5_oppCiv).setImageDrawable(getDrawable(resR1))
How could i get this image resource of it ? And use it for the other ImageButton?
You should be setting the image like using setImageDrawable like this.
val image = findViewById<ImageButton>(R.id.your_view_id)
image.setOnClickListener {
findViewById<ImageButton>(idR5_oppCiv).setImageDrawable(image.drawable)
}

In .NET MAUI is there a way to choose a different XAML view based upon whether the device is in Landscape or Portrait

I am using .NET MAUI and I have a particular view that is rather complicated and I would rather have a different layout if the device orientation is in Portrait vs if the device orientation is in landscape.
I tinkered around with Android programming a long time ago and for Android Studio there was a way to choose a XAML file when the device was in landscape and a different XAML file when the device was in portrait.
Is this possible with MAUI?
If not what is the best practice in regards to this?
Here is my layout and in landscape mode I can fit 3 major sections in one row but this won't work in portrait and in portrait I would like the middle major element to be on the next row.
Here are examples of my portrait vs landscape mockup I created on Photoshop:
UPDATE WITH SOLUTION***************
I'm attempting the solution that FreakyAli posted and have a mostly working prototype, so anyone who is wanting to use a different XAML layout based upon the screen orientation can use this approach.
I created a new folder called "ContentViews" in my solution.
I added 3 new ContentViews (the XAML with the code behind):
HomePageLandscape
HomePagePortrait
HomePageOrientationViewLoader
The HomePageOrientationViewLoader will get loaded directly into the HomePage.xaml file later on. This is the control that will load either the HomePagePortrait ContentView when in portrait mode or HomePageLandscape ContentView when in landscape mode.
namespace ScoreKeepersBoard.ContentViews;
public partial class HomePageOrientationViewLoader : ContentView
{
public ContentView homePagePortraitContentView;
public ContentView homePageLandscapeContentView;
public HomePageOrientationViewLoader()
{
InitializeComponent();
homePagePortraitContentView = new HomePagePortrait();
homePageLandscapeContentView = new HomePageLandscape();
this.Content = homePageLandscapeContentView;
DeviceDisplay.Current.MainDisplayInfoChanged += Current_MainDisplayInfoChanged;
this.Content = DeviceDisplay.Current.MainDisplayInfo.Orientation == DisplayOrientation.Portrait ? homePagePortraitContentView : homePageLandscapeContentView;
}
private void Current_MainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{
if (e.DisplayInfo.Orientation == DisplayOrientation.Landscape)
{
// if (this.Content.GetType() is not typeof(HomePageLandscape))
// {
this.Content = homePageLandscapeContentView;
// }
}
else if (e.DisplayInfo.Orientation == DisplayOrientation.Portrait)
{
// if (this.Content.GetType() is not typeof(HomePagePortrait))
// {
this.Content = homePagePortraitContentView;
// }
}
else
{
//Whatever you would like to do if the orientation is unknown.
}
}
}
The HomePageOrientationViewLoader.xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScoreKeepersBoard.ContentViews.HomePageOrientationViewLoader">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentView>
Here is the HomePagePortrait.xaml.cs file:
namespace ScoreKeepersBoard.ContentViews;
public partial class HomePagePortrait : ContentView
{
public HomePagePortrait()
{
InitializeComponent();
}
}
Here is the HomePagePortrait.xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScoreKeepersBoard.ContentViews.HomePagePortrait">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI portrait"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentView>
Here is the HomePageLandscape.xaml.cs file:
namespace ScoreKeepersBoard.ContentViews;
public partial class HomePageLandscape : ContentView
{
public HomePageLandscape()
{
InitializeComponent();
}
}
Here is the HomePageLandscape.xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScoreKeepersBoard.ContentViews.HomePageLandscape">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI landscape"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentView>
My project had an initial home Content Page called HomePage. We are loading the HomePageOrientationViewLoader ContentView into the xaml of HomePage Content Page as a custom control. Note that I had to define the namespace that the ContentViews were located in and use that when defining the control in the xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:ScoreKeepersBoard.ContentViews"
x:Class="ScoreKeepersBoard.Views.HomePage"
Title="HomePage">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI Home Page Content Page"
VerticalOptions="Center"
HorizontalOptions="Center" />
<controls:HomePageOrientationViewLoader></controls:HomePageOrientationViewLoader>
</VerticalStackLayout>
</ContentPage>
Here is the code behind for the home page
namespace ScoreKeepersBoard.Views;
public partial class HomePage : ContentPage
{
public HomePage(HomeViewModel homeViewModel)
{
InitializeComponent();
}
}
and when the project runs on my iphone simulator in portrait mode:
You will see the second label shown says "Welcome to .NET MAUI portrait" which is the view from the portrait content view and when I switch to landscape:
You will see the second label shown says "Welcome to .NET MAUI landscape" which is the view from the landscape content view.
ISSUES
This works on my iPhone simulator but when I switch to my Android pixel 5 simulator and toggle my switch phone orientation it doesn't work and putting in line breaks the code defined in HomePageOrientationViewLoader is not triggered. NEW NOTE: I tried this on a physical Android phone and it is working so it must have just been the emulator.
I will need to use this for a non trivial example that has a view model that will be holding data on a sports game score, timing, etc. I guess I will just need to inject a singleton of the view model into each and they will just share and if the orientation switches the other Content View will load and the view model will bind to the appropriate controls?
The initial suggested code by FreakyAli had this check:
if (e.DisplayInfo.Orientation == DisplayOrientation.Landscape)
{
if (this.Content.GetType() is not typeof(HomePageLandscape))
{
this.Content = homePageLandscapeContentView;
}
}
but the part "typeof(HomePageLandscape) gives me an error and says a constant is expected.
Other than that the different views for different orientations is working and I thank FreakyAli mightily! I am sure I will figure out why the Android emulator is not triggering the orientation switch code, but suggestions would be awesome.
Ideally this is how i would handle such a scenario:
In my constructor, I would get the DisplayInfoChanged event which notifies me if this info changes and i would also assign my current ContentView based on the current Orientation:
DeviceDisplay.Current.MainDisplayInfoChanged += Current_MainDisplayInfoChanged;
this.Content = DeviceDisplay.Current.MainDisplayInfo.Orientation == DisplayOrientation.Portrait ? potraitView : landscapeView;
Here PortraitView is a ContentView that is the View I would display when my device is in Portrait and Vice Versa.
And then handle the runtime change of the orientation as follows:
private void Current_MainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{
if(e.DisplayInfo.Orientation==DisplayOrientation.Landscape)
{
if(this.Content.GetType() is not typeof(LandscapeView))
{
this.Content = landscapeView;
}
}
else if (e.DisplayInfo.Orientation == DisplayOrientation.Portrait)
{
if (this.Content.GetType() is not typeof(PortraitView))
{
this.Content = portraitView;
}
}
else
{
//Whatever you would like to do if the orientation is unknown.
}
}
Hope this helps you!
The proper way to do this is through ContentViews where you have 2 ContentViews, one for portrait and one for landscape. You have another ContentView that is used to choose to load in either landscape or portrait depending on the orientation.
I created a tutorial that puts all of the pieces together:
https://codeshadowhand.com/net-maui-different-layouts-for-portrait-vs-landscape/
Thanks a million to FreakyAli for pointing me in the right direction!!!

Connecting Xamarin Form tabbed pages to XAML

I am fairly new to all of this and am currently attempting to get the height of the tabs in the Xamarin tabbed page form. The only solution I found to this is to write a custom renderer, and that is what I'm having a hard time with.
After a couple days of struggling I managed to get to this spot (hopefully on the right track), however I just cannot understand how to connect the XAML to my custom tabbed page. This is what I have so far.
GameTab.xaml
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Diplomacy.Views.GameTab"
xmlns:pages="clr-namespace:Diplomacy.Views"
xmlns:custom="clr-namespace:Diplomacy.CustomRenderers">
<!--Pages can be added as references or inline-->
<TabbedPage.Children>
<pages:TabbedMap Title="Map" Icon="tank.png"/>
<pages:TabbedChat Title="Chat" Icon="chat.png"/>
</TabbedPage.Children>
</TabbedPage>
GameTab.xaml.cs
namespace Diplomacy.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class GameTab : Xamarin.Forms.TabbedPage
{
SelectionGamesViewModel viewModel;
public GameTab(SelectionGamesViewModel viewModel)
{
InitializeComponent();
// Disables switching between tabs with the swipe gesture
On<Xamarin.Forms.PlatformConfiguration.Android>().DisableSwipePaging();
// Sets the tab at the bottom in android phones
On<Xamarin.Forms.PlatformConfiguration.Android>().SetToolbarPlacement(ToolbarPlacement.Bottom);
BindingContext = this.viewModel = viewModel;
}
}
MyCustomRenderer.cs
namespace Diplomacy.CustomRenderers
{
public class CustomTabbedPage : Xamarin.Forms.TabbedPage
{
}
}
At this point my next step is to use the CustomTabbedPage (correct me if I'm wrong from here on out).
With this line: xmlns:custom="clr-namespace:Diplomacy.CustomRenderers"
I should be able to wedge myself into the Xamarin Tabbed Page form with my custom render, which currently does nothing.
The way that I believe this is done is by changing TabbedPage to CustomTabbedPage like shown below
<?xml version="1.0" encoding="utf-8" ?>
<custom:CustomTabbedPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Diplomacy.Views.GameTab"
xmlns:pages="clr-namespace:Diplomacy.Views"
xmlns:custom="clr-namespace:Diplomacy.CustomRenderers">
<!--Pages can be added as references or inline-->
.
. // Same stuff goes here
.
</custom:CustomTabbedPage>
However when I do that, I get all sorts of errors in GameTab.xaml.cs and 1 error in the navigation page trying to push GameTab (the 2nd error)
I've been struggling probably for weeks now, I really need some help on how to set up this custom render. I get the theory of what it does and what is it's purpose, however I don't fully understand how the compiler handles it all, and how to link it all together. Please and thank you. Sorry for the long question, I just wanted to be thorough.
EDIT:
This is the Android custom renderer code that lives in Diplomacy.Android
[assembly: ExportRenderer(typeof(CustomTabbedPage), typeof(MyTabbedPage))]
namespace Diplomacy.Droid.CustomRenderer
{
public class MyTabbedPage : TabbedRenderer
{
public MyTabbedPage(Context context) : base(context)
{
}
}
}
All the errors that you get are for one and one reason alone that the other part of your partial class i.e. GameTab.xaml.cs files GameTab class is not inheriting from your CustomTabbedPage but your Xamarin.Forms.TabbedPage
All you have to do is something like this in your GameTab.xaml.cs
public partial class GameTab : Diplomacy.CustomRenderers.CustomTabbedPage

How to detect hitting the bottom of a grid within a scrollview in Xamarin Forms

I am using Xamarin.Forms to display Items from a Websource in a Grid (within a ScrollView).
When the user hits the bottom of the grid, i want to load more items and add them to the grid. I know that usually a ListView is preferred for displaying data in this fashion (and that ListView has an ItemAppearing event) but sadly i have to use a grid.
If it helps, i could post some code, but im not sure if that is necessary here.
Thanks in advance
Edit: here is my really boring layout file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyProject.MyNamespace.LayoutName"
SizeChanged="OnPageSizeChanged">
<ScrollView >
<AbsoluteLayout x:Name="myLayout">
<Grid x:Name="GridForItems" RowSpacing="6" ColumnSpacing="6">
</Grid>
</AbsoluteLayout>
</ScrollView>
</ContentPage>
I add all the rows, columns and items programmatically.
Since i had not gotten a satisfying answer to my original question (how to detect hitting the bottom of a scrollview) and i have not found a solution anywhere else on the net i am going to post how i solved this.
Note that this is probably the not an elegant solution and, if you have a better answer, please post it and i will accept it as the best answer.
What i did was implement a delegate for the Scrolled Event of ScrollView.
myScrollView.Scrolled += (sender, e) => { onScrolled(); };
Within my onScrolled i have the following if-clause to determine whether i have hit the bottom of the ScrollView:
private void onScrolled()
{
if(myScrollView.ScrollY >= myScrollView.ContentSize.Height - myScrollView.Height)
{
//Handle hitting the bottom
}
}
When you have a list of items you should use a ListView to display it. In the DataTemplate of this ListView you can specify how to display each item (use a grid there if you really need a grid).
In the Listview, you can implement following code to detect the bottom:
myListview.ItemAppearing += (sender, e) =>
{
// CHECK HERE
if(e.Item.Id == MyItems[MyItems.Count - 1].Id)
{
// YOU HIT THE BOTTOM
}
}
Why should I use Listview? Look at this forum thread about Dynamic Grids.

UI Page inside class library

I am developing a class library in Windows 8 (C#), in which i require to show an UI to get user input. How to create UI inside class library and invoking it. Please help.
Now I am invoking the Popup from class library to show the required UI, but I found the popup is opening from library but its hiding below the other UI element (Webview in my case).Please refer the code snippet below.
Class library code
namespace PopUpLibrary
{
public class PopupDialog
{
public void ShowPopup()
{
Popup popup = new Popup();
popup.HorizontalAlignment = HorizontalAlignment.Center;
popup.VerticalAlignment = VerticalAlignment.Center;
popup.Height = 500;
popup.Width = 700;
Button button = new Button();
button.Content = "adfadfad";
button.Width = 200;
button.Height = 100;
popup.Child = button;
popup.IsOpen = true;
}
}
}
Application code:
MainPage.xaml
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<WebView Name="webview" ></WebView>
</Grid>
MainPage.xaml.cs
public MainPage()
{
this.InitializeComponent();
webview.Navigate(new Uri("http://www.google.com"));
PopupDialog popupdialog = new PopupDialog();
popupdialog.ShowPopup();
}
You cannot show other controls on top of the WebView. However, you can work around this by temporarily hiding the WebView and displaying a WebViewBrush.
From the WebView Documentation:
WebView has the characteristic that other UI regions such as controls cannot be rendered on top of the WebView. This is because of how window regions are handled internally, particularly how input events are processed and how the screen draws. If you want to render HTML content and also place other UI elements on top of that HTML content, you should use WebViewBrush as the render area. The WebView still provides the HTML source information, and you reference that WebView through the SourceName property. WebViewBrush does not have this overlay limitation.
If you want to display an interactive WebView that only occasionally has overlapping content (such as a drop-down list or app bar), you can temporarily hide the WebView control when necessary, replacing it with an element using a WebViewBrush fill. Then, when the overlapping content is no longer present, you can display the original WebView again. For more info, see the WebView control sample.
What kind of input? Yes/No or string input?
For Yes/No you could use the standard MessageDialog. If you need to receive custom input, such as text, you could use the CustomDialog control, available in the Callisto toolkit.