HorizontalTextAlignment - iOS (Not working as intended) - xamarin

I have a problem when using HorizontalTextAlignment="Center" on iOS cause it is properly working on Android. The problem is that it is not properly wrapping the sentence to next line as intended.
This is my result:
Code:
<Grid HorizontalOptions="CenterAndExpand" VerticalOptions="Center" Grid.Row="3">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding lblItem}"
TextColor="#BCBCBC"
FontFamily="Calibri"
Grid.Row="0"
Grid.Column="0"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
HorizontalOptions="Center">
<Label.Margin>
<OnIdiom x:TypeArguments="Thickness">
<OnIdiom.Tablet>
<OnPlatform x:TypeArguments="Thickness" iOS="0,5,0,0" Android="0,5,0,0" WinPhone="0,0,0,0"/>
</OnIdiom.Tablet>
<OnIdiom.Phone>
<OnPlatform x:TypeArguments="Thickness" iOS="0,0,0,0" Android="0,0,0,0" WinPhone="0,0,0,0"/>
</OnIdiom.Phone>
</OnIdiom>
</Label.Margin>
</Label>
</Grid>
How can I properly use the HorizontalTextAlignment="Center" to properly wrap it around in a layout?
EDIT: Seems like there is something wrong when wrapping a text that is binded behind the vm, for now I do not know why but, I am trying to look for work around. Gonna keep this post updated if ever I found a solution.

In order to wrap text around, you'll have to set the Label.LineBreakMode property to WordWrap (see here and here).
<Grid HorizontalOptions="CenterAndExpand" VerticalOptions="Center" Grid.Row="3">
<!-- ... -->
<Label Text="{Binding lblItem}"
LineBreakMode="WordWrap"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
HorizontalOptions="Center">
<!-- ... -->
</Label>
</Grid>
This should break the text correctly.
Maybe you have to set the HorizontalOptions to Fill instead of Center.

I have fixed the problem, seems like the Label's Horizontal/Vertical.TextAlignment has a bug on iOS if you are binding the Label's Text. In order to fix this I created a CustomRenderer for this label specifically on iOS:
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null) return;
var ctrl = (YourLabel)Element;
ctrl.HorizontalTextAlignment = TextAlignment.Center;
ctrl.VerticalTextAlignment = TextAlignment.Center;
}

Related

How to Center an image in each column inside one row in Xamarin forms?

I know I have already asked this question here: How to Center an image in each column inside one row in Xamarin?, but I didnt have access to an actual device that time, so I just run the emulator and it looks like this:
But now that I have a phone to run it, the page now looks like this:
This is my code for this page:
<StackLayout>
<RelativeLayout>
<Grid Margin="0,10,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<StackLayout Grid.Row="0" Grid.Column="0" HorizontalOptions="CenterAndExpand">
<Image x:Name="ImgSrcMale"
HeightRequest="165"
Source="male"
WidthRequest="200"
Aspect="AspectFit"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer x:Name="MaleClick"
NumberOfTapsRequired="1"
Tapped="MaleClick_Tapped"/>
</Image.GestureRecognizers>
</Image>
</StackLayout>
<StackLayout Grid.Row="0" Grid.Column="1" HorizontalOptions="CenterAndExpand">
<Image Source="female1"
WidthRequest ="200"
HeightRequest="165"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Aspect="AspectFit"
x:Name="ImgSrcFemale">
<Image.GestureRecognizers>
<TapGestureRecognizer x:Name="FemaleClick"
NumberOfTapsRequired="1"
Tapped="FemaleClick_Tapped"/>
</Image.GestureRecognizers>
</Image>
</StackLayout>
</Grid>
<Grid Margin="0,178,0,0">
<ScrollView>
<StackLayout>
<Grid VerticalOptions="CenterAndExpand" Margin="20,0,20,0" RowSpacing="20">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Text="Gender"
FontSize="Title"
TextColor="WhiteSmoke"
HorizontalOptions="Center"
FontAttributes = "Bold"
Grid.Row="0"/>
<local:RoundedEntry x:Name="EntryUsername"
Placeholder="Username"
TextColor="WhiteSmoke"
PlaceholderColor="WhiteSmoke"
Grid.Row="1"/>
<local:RoundedEntry x:Name="EntryPassword"
Placeholder="Password"
IsPassword="True"
TextColor="WhiteSmoke"
PlaceholderColor="WhiteSmoke"
Grid.Row="2"/>
<local:RoundedEntry x:Name="EntryEmail"
Placeholder="Email"
Keyboard="Email"
TextColor="WhiteSmoke"
PlaceholderColor="WhiteSmoke"
Grid.Row="3"/>
<Button Text="Sign Up"
HorizontalOptions="CenterAndExpand"
TextColor="WhiteSmoke"
BackgroundColor="Coral"
WidthRequest="150"
Clicked="Button_Clicked"
CornerRadius="25"
FontAttributes = "Bold"
FontSize = "Large"
Grid.Row="4"/>
<Label x:Name="GenderLabel"
Text=""
TextColor="WhiteSmoke"
IsVisible="False"/>
</Grid>
</StackLayout>
</ScrollView>
</Grid>
</RelativeLayout>
</StackLayout>
You could easily create this layout with one grid. Remove the top-level StackLayout and RelativeLayout. You may want to put the whole grid inside a scrollview to allow the screen to move up with the keyboard.
I'd recommend a grid with two columns and six rows (the controls at the bottom will span both columns. Define the columns with width * (not sure why you had 5*). You don't need to wrap the images in anything, just place them in the grid directly.

BindableBase not working on CollectionView

I have a CollectionView on my project with the most stable version of Xamarin Forms that supports CollectionView (4.3.0.908675) with the following code below.
<CollectionView x:Name="ScrollButtons"
ItemsSource="{Binding MenuItems}"
SelectedItem="{Binding SelectedMenuItem, Mode=TwoWay}"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
HeightRequest="90"
SelectionMode="Single"
SelectionChangedCommand="{Binding MenuItemSelectedCommand}"
BackgroundColor="{DynamicResource BackgroundColorShell}">
<CollectionView.Footer>
<!--HACK to keep showing last item on CollectionView -->
<BoxView BackgroundColor="Transparent" HeightRequest="90" WidthRequest="50"/>
</CollectionView.Footer>
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Horizontal"
Span="1" HorizontalItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid WidthRequest="90" HeightRequest="90" Padding="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Frame Grid.Column="0"
Grid.Row="0"
BorderColor="Black"
BackgroundColor="{Binding BackgroundColor}"
>
</Frame>
<StackLayout Padding="5" Grid.Row="0" Grid.Column="0">
<Label Text="{Binding Text}"
TextColor="{Binding TextColor}"
LineBreakMode="WordWrap"
FontSize="{StaticResource BaseFontSize}"
x:Name="tileLabel">
</Label>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" VerticalOptions="EndAndExpand">
<Image Source="{Binding SecondaryIconSource}"
HorizontalOptions="Start"
VerticalOptions="EndAndExpand"
WidthRequest="25"
HeightRequest="25"
IsVisible="{Binding IsSecondaryIconVisible}"
/>
<Image Source= "{Binding ImageIcon}"
HorizontalOptions="EndAndExpand"
VerticalOptions="EndAndExpand"
WidthRequest="25"
HeightRequest="25"
x:Name="tileIcon">
</Image>
</StackLayout>
</StackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
OnMenuSelectedItemCommand
private async Task OnMenuItemSelected()
{
Console.WriteLine("OnMenuItemSelected");
await NavigationService.NavigateAsync($"{SelectedMenuItem.NavigationPath}");
HighlightedMenuItem = SelectedMenuItem;
SelectedMenuItem = null;
}
The CollectionView consists of a collection of BottomMenuItem class which inherits from BindableBase for Prism. My goal is to change the properties of a BottomMenuItem as its selected on the CollectionView. However, the collection view is acting weird and it only changes based on BottomMenuItem that is not currently in the screen. As shown below, it only works on the 5th item and beyond, items that are not initially loaded on the screen.
Any help would be greatly appreciated. Thank you!
I figured it out. The problem is that my code relies on the scrolling to an item in order for a selectedItem to be highlighted. I made an assumption that the bug is based on item cells that are initially loaded are not working but in reality it was items that doesn't need scrolling.
Based on my OnMenuItemSelected, I pass the highlighted item to the next page. Handle that logic with the OnNavigatingTo logic below.
public override async void OnNavigatingTo(INavigationParameters parameters)
{
base.OnNavigatingTo(parameters);
Console.WriteLine("OnNavigatingTo");
HighlightedMenuItem = parameters.GetValue<BottomMenuItem>("highlightedMenuItem");
foreach (var item in MenuItems)
{
item.IsActive = false;
}
if (HighlightedMenuItem != null)
{
Console.WriteLine("OnNavigatingTo HighlightedItem - {0}", HighlightedMenuItem.Text);
HighlightedMenuItem.IsActive = true;
}
}

How to change the style of tab in segment control in xamarin forms

I have a segment control where I'm displaying tabs. But I'm not able to edit the style of the tab in Xamarin Forms. This is the UI I want
This is how I want to display my tabs in the segment control. I'm able to change the tint color, background color, and text color but none of that will get me a tab in this style. This is my current UI
This is XAML code where I implemented the segment control
<controls:SegmentedControl BackgroundColor="White" SelectedTextColor="Black" TintColor="#FFA500" x:Name="SegControl" ValueChanged="Handle_ValueChanged">
<controls:SegmentedControl.Children>
<controls:SegmentedControlOption Text="VENDOR NAME" />
<controls:SegmentedControlOption Text="PRODUCT/SERVICE" />
</controls:SegmentedControl.Children>
</controls:SegmentedControl>
<StackLayout x:Name="SegContent" />
</StackLayout>
<StackLayout Margin="0,30,0,0">
<StackLayout AbsoluteLayout.LayoutBounds=".20,1,1,.1" AbsoluteLayout.LayoutFlags="All" BackgroundColor="White" HorizontalOptions="FillAndExpand" Orientation="Horizontal">
<StackLayout Style="{StaticResource ButtonNavigationBarStackLayoutStyle}" x:Name="stckNear">
<Image Margin="0,10,0,10" x:Name="imgNear" Style="{StaticResource ButtonNavigationBarImageStyle}" />
<Label Text="Near" Style="{StaticResource ButtonNavigationBarLabelStyle}"></Label>
</StackLayout>
<StackLayout Style="{StaticResource ButtonNavigationBarStackLayoutStyle}" x:Name="stckSearch">
<Image Margin="0,10,0,10" x:Name="imgSearch" Style="{StaticResource ButtonNavigationBarImageStyle}" />
<Label Text="Search" Style="{StaticResource ButtonNavigationBarLabelStyle}"></Label>
</StackLayout>
<StackLayout Style="{StaticResource ButtonNavigationBarStackLayoutStyle}" x:Name="stckCart">
<Image Margin="0,10,0,10" x:Name="imgCart" Style="{StaticResource ButtonNavigationBarImageStyle}" />
<Label Text="Cart" Style="{StaticResource ButtonNavigationBarLabelStyle}"></Label>
</StackLayout>
<StackLayout Style="{StaticResource ButtonNavigationBarStackLayoutStyle}" x:Name="stckAccount">
<Image Margin="0,10,0,10" x:Name="imgAccount" Style="{StaticResource ButtonNavigationBarImageStyle}" />
<Label Text="Account" Style="{StaticResource ButtonNavigationBarLabelStyle}"></Label>
</StackLayout>
</StackLayout>
I'm not using any custom renderer for this segment control. Do I have to use a custom renderer for implementing the required UI? If yes how? Any suggestions?
SegmentedControl is not a built in Xamarin.Forms control. There are a few libraries that offer a SegmentedControl, so it would help to know which one you are using.
That said, the library author who created that SegmentedControl also made the platform renderers and so the different look on iOS vs Android is a result of that.
You can, of course, create your own custom renderer, but then why use the library?
Easier to me would be to make a control using Xamarin Forms, for instance you can use a grid that has a first row of two labels ( or Buttons) and a second row of 2 BoxViews that can act as the underline (very short height). Then just add TapGestureRecognizers to each Label (or just use a Buttons and style as needed).
Here's an example using Buttons and BoxViews:
XAML:
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="vBtn"
Text="VENDOR NAME" Clicked="Handle_Clicked"
TextColor="Black"
BackgroundColor="Transparent"
BorderColor="Transparent"
Grid.Row="0"
Grid.Column="0"/>
<Button x:Name="pBtn"
Text="PRODUCT/SERVICE" Clicked="Handle_Clicked"
TextColor="Black"
BackgroundColor="Transparent"
BorderColor="Transparent"
Grid.Row="0"
Grid.Column="1" />
<BoxView x:Name="vBox"
Color="#FFA500" HeightRequest="5"
Grid.Row="1"
Grid.Column="0"/>
<BoxView x:Name="pBox"
Color="Silver" HeightRequest="5"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
Code behind:
void Handle_Clicked(object sender, System.EventArgs e)
{
Button btn = sender as Button;
if (btn.Text == "PRODUCT/SERVICE")
{
vBox.Color = Color.Silver;
pBox.Color = Color.FromHex("#FFA500");
// Do anything else you need to do when the PRODUCT/SERVICE is tapped
}
else
{
vBox.Color = Color.FromHex("#FFA500");
pBox.Color = Color.Silver;
// Do anything else you need to do when the VENDOR NAME is tapped
}
}
No library or custom renderer needed.

Xamarin forms custom scrollview showing weird behavior

I have a customized ScrollView as per the requirement which is based on the TLScrollView.
Now when I am adding some data to it there is weird behaviour that I noticed recently a part of the screen area is unclickable, And the rest of the part works fine.
Now the same code works like a charm on Android but iOS is showing this weird behaviour,
Xaml:
<controls:TLScrollView Orientation="Horizontal" BackgroundColor="White"
x:Name="ListAddons" ItemsSource="{Binding ListAddons}" AbsoluteLayout.LayoutFlags = "All"
AbsoluteLayout.LayoutBounds = "0.5, 0.5, 1.0, 1.0">
<controls:TLScrollView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="Transparent" Margin = "0, 5, 0, 0">
<Grid BackgroundColor="White" InputTransparent="true" RowSpacing = "0" ColumnSpacing = "0">
<Grid.RowDefinitions>
<RowDefinition Height="0.5*" />
<RowDefinition Height="45.0*" />
<RowDefinition Height="45*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="1" />
</Grid.ColumnDefinitions>
<ffimageloading:CachedImage Source="{Binding Icon, Converter={StaticResource Base64ToImageConverter}}"
ErrorPlaceholder = "nopreviewlandscape" LoadingPlaceholder = "loadingicon"
Grid.Row="1" Grid.Column="0" HorizontalOptions="Center"
VerticalOptions="Center" Aspect="AspectFill" />
<Label x:Name="lblRecommendationsName" Margin="0" Text="{Binding CategoryName}"
LineBreakMode="WordWrap" XAlign="Center" TextColor="{StaticResource gray_text_color}"
VerticalOptions="Center" HorizontalOptions="Center" Grid.Row="2"
Grid.Column="0" Style="{StaticResource RecommendationName}">
<Label.FontFamily>
<OnPlatform x:TypeArguments="x:String">
<On Platform="iOS" Value="OpenSans-Light" />
<On Platform="Android" Value="OpenSans-Light" />
</OnPlatform>
</Label.FontFamily>
<Label.TextColor>
<OnPlatform x:TypeArguments="Color">
<On Platform="Android" Value="#000000"></On>
</OnPlatform>
</Label.TextColor>
</Label>
<BoxView WidthRequest="0" Grid.Row="1" Grid.Column="1"
BackgroundColor="{StaticResource separator_color}"
Grid.RowSpan="4" Style="{StaticResource BoxViewHomeStyle}">
</BoxView>
</Grid>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.AddonsClickCommand,Source={x:Reference ListAddons}}"
CommandParameter="{Binding CategoryId}"
/>
</StackLayout.GestureRecognizers>
</StackLayout>
</ViewCell>
</DataTemplate>
</controls:TLScrollView.ItemTemplate>
</controls:TLScrollView>
First, I thought there must be some sort of an overlapping in there which might be causing this but then I realized that the same code was working on Android devices and causing this on iOS.
The part of the image that is unclickable is inside the black box everywhere else the click event works well.
It may have nothing to do with this problem but I think "Grid.RowSpan" of BoxView may cause problem.
The Grid have only three rows(0-2 rows) definitions but BoxView specified to merge 1 to 4 rows.
<Grid.RowDefinitions>
<RowDefinition Height="0.5*" />
<RowDefinition Height="45.0*" />
<RowDefinition Height="45*" />
</Grid.RowDefinitions>
<BoxView WidthRequest="0" Grid.Row="1" Grid.Column="1"
BackgroundColor="{StaticResource separator_color}"
Grid.RowSpan="4" Style="{StaticResource BoxViewHomeStyle}">
</BoxView>
Well, I am not sure what was causing the issue but updating to the latest version of Xamarin.Forms fixed the bug!
I updated to Xamarin.Forms version 4.5.0.396
Feel free to get back if you are facing a similar issue.

Xamarin iOS images overlapping inside Grid

Heyo,
So in Xamarin I have a <Grid> with an <Image> and a couple <Label>s inside it, all wrapped inside a <ViewCell>. This looks totally fine in Xamarin.Android, however in Xamarin.iOS the images overlap the labels. I'm not sure what the difference could be - why does it look good in Xamarin.Android but in iOS its all wonky?
Below is my XAML and a couple mockups to show what I mean.
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Column="0" Grid.Row="0" Aspect="AspectFill" Source="{Binding ImageOverlayEN}" />
<Label Grid.Column="0" Grid.Row="1" VerticalTextAlignment="Start" LineBreakMode="WordWrap" Text="{Binding DynamicOfferText}" FontSize="18">
<Label.FontFamily>
<OnPlatform x:TypeArguments="x:String">
<OnPlatform.iOS>RobotoCondensed-Regular</OnPlatform.iOS>
<OnPlatform.Android>RobotoCondensed-Regular.ttf#RobotoCondensed-Regular</OnPlatform.Android>
<OnPlatform.WinPhone>RobotoCondensed-Regular.ttf#RobotoCondensed</OnPlatform.WinPhone>
</OnPlatform>
</Label.FontFamily>
</Label>
<Label Grid.Column="0" Grid.Row="2" VerticalTextAlignment="Start" LineBreakMode="WordWrap" Text="{Binding DynamicOfferDetail}" FontSize="16">
<Label.FontFamily>
<OnPlatform x:TypeArguments="x:String">
<OnPlatform.iOS>RobotoCondensed-Regular</OnPlatform.iOS>
<OnPlatform.Android>RobotoCondensed-Regular.ttf#RobotoCondensed-Regular</OnPlatform.Android>
<OnPlatform.WinPhone>RobotoCondensed-Regular.ttf#RobotoCondensed</OnPlatform.WinPhone>
</OnPlatform>
</Label.FontFamily>
</Label>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
I tried setting the HeightRequest but that didn't seem to make a difference:
#if __IOS__
if (viewModel.Items.Count > 0)
{
MyListView.HeightRequest = 300 * viewModel.Items.Count;
}
#endif
Here is a visual representation of what is happening:
Figured it out - had to do with my RowDefinitions being all set to Auto
This got it looking just right:
<RowDefinition Height="1*" />
<RowDefinition Height="0.18*" />
<RowDefinition Height="0.77*" />
I think what was happening is the ListView renders first, the Auto row definition heights set the height of each row before the image is done loading, then the image finishes loading and overlaps the rest of the list view's item (including the labels). This might just be a quirk of iOS, where as Android calculates the row heights after the images are done loading (explaining why it looked fine in Android)

Resources