Angular Nativescript - Using a ScrollView within an AbsoluteLayout - ios

I'm building a photo browser app where the user should swipe between pages to see different photos. Here is the view that currently works for me.
<ScrollView orientation="horizontal" ios.pagingEnabled="true" >
<StackLayout orientation="horizontal">
<Image *ngFor="#picture of buffer" [src]="picture.originUrl" stretch="aspectFit"></Image>
</StackLayout>
</ScrollView>
The problem comes when I try to apply an overlay. So I tried to wrap the whole thing in an AbsoluteLayout so that I could have the ScrollView operate as normal but keep the overlay in place on top. When I do this scrolling breaks all-together. Here is the view that breaks scrolling:
<AbsoluteLayout>
<Label text="overlay" left="0" tope="0"></Label>
<ScrollView orientation="horizontal" ios.pagingEnabled="true" left="0" top="0">
<StackLayout orientation="horizontal">
<Image *ngFor="#picture of buffer" [src]="picture.originUrl" stretch="aspectFit"></Image>
</StackLayout>
</ScrollView>
</AbsoluteLayout>
The layouts appear to work correctly, but scrolling breaks.
Anybody have any ideas on how to accomplish this?

If I was doing this layout I would do this:
<GridLayout rows="*">
<ScrollView row="0" orientation="horizontal" ios.pagingEnabled="true">
<StackLayout orientation="horizontal">
<Image *ngFor="#picture of buffer" [src]="picture.originUrl" stretch="aspectFit"></Image>
</StackLayout>
</ScrollView>
<Label row="0" text="overlay"></Label>
</GridLayout>
By using the same row they will be positioned in the same place. I use this technique to create a entire search interface that will overlay part of the screen when they click the search button. Please note; the items later in the GridLayout show above items earlier in the GridLayout. So that is why the Label was moved to the bottom of the GridLayout, so it will be visible above the ScrollLayout.
Here is the actual test template I did:
<Page id="Page" onloaded="pageLoaded" class="">
<GridLayout rows="100,*">
<ScrollView row="0">
<StackLayout>
<Label visibility="{{ pageData.visible ? 'visible' : 'collapsed' }}" text="Hi1 this should appear/disappear" class ="lab1"/>
<Label visibility="{{ pageObs.visible ? 'visible' : 'collapsed' }}" text="Hi2 this should appear/disappear" class ="lab1"/>
<Label visibility="{{pageData, pageData.visible ? 'visible' : 'collapsed' }}" text="Hi3 this should appear/disappear" class ="lab1"/>
<Label left="10" top="70" visibility="{{visible ? 'visible' : 'collapsed' }}" text="Hi4 this should appear/disappear" class ="lab1"/>
<Label text="{{pageObs.visible}}"/>
<Label text="I'm at 120"/>
<Button text="tap" tap="onTap"/>
<Label text="Another text"/>
<Label text="Another text 2"/>
</StackLayout>
</ScrollView>
<Label row="0" text="Overlay"/>
<Label row="1" text="Grid row 1"/>
</GridLayout>
</Page>
This layout has two Grid rows, so that you can see where the ScrollView ends; the first row has the ScrollView AND the "Overlay"

I got an answer from a bug I filed: https://github.com/NativeScript/nativescript-angular/issues/184
The problem comes from the fact that AbsoluteLayout measures all its children with infinity space, so the ScrollView is as big as its content and indeed scrolling will not work. A possible fix is it to set a size for the ScrollView. In this specific case % layout size can be used.
<AbsoluteLayout>
<Label text="overlay" left="0" top="0"></Label>
<ScrollView orientation="horizontal" ios.pagingEnabled="true" left="0" top="0" width="100%" height="75%">
<StackLayout orientation="horizontal">
<Image *ngFor="#picture of pictures" [src]="picture.originUrl" stretch="aspectFit"></Image>
</StackLayout>
</ScrollView>
</AbsoluteLayout>
Both % values are % from the size that parent layout control is measured (AbsoluteLayout). Since Absolute layout is a root element actually these values will be % of the screen size.

Related

Inserting multiple RadListViews does not show on iOS

I need Multiple RadListViews on the same page, I'm using NativeScript Angular
I don't know the number of items in each list, and the items might be of very different size depending on the amount of text the item contains.
So to speak, I need three RadLists on top of each other - the first might have 3 items, the next 72 and the last 9 items.
I want each list to take op the height its contents demands, so I can show them on top of each other, no matter how long they are.
On Android it works like a charm - each radlistview takes up in exactly the height needed to show its items and produces the next radlistview right after
On iOS, on the other hand, it's very different - if I do not set the height of the RadListView itself it simply does not show.
I have the two RadListViews living inside a GridLayout, and giving the rows an absolute value like row="150, 150" or even rows="*,*" will show as much of the list as the provided values permits - but I need it to be rows="auto, auto" as I have no idea how high each list is gonna be, and I want to show all the items.
Setting the height directly on the RadListView will also work, but again - I don't know the height.
Setting height="100%" directly on the RadListView does not solve my problem as I cannot have an absolute parent height.
So is this possible at all, or do I have to calculate a number for the height property (which I really really don't want to do)
Again... the problem only exists on iOS devices.
I have tried feeding the dataItem both as ObservableArray and as a plain array of objects type: name[], because I thought that the asynchronicity of the ObservableArray might have a role to play, but it does not matter - I can even fill the text property with a constant like instead of . Still, if no height is set on either the row or the RadListView itself - nothing will show up.
Regards Per
<GridLayout rows="auto, auto'>
<RadListView [items]=" dataItems">
<ng-template tkListItemTemplate let-item="item">
<StackLayout orientation="vertical">
<Label [text]="item.firstname"></Label>
<Label [text]="item.lastname"></Label>
</StackLayout>
</ng-template>
</RadListView>
<RadListView [items]="dataItems">
<ng-template tkListItemTemplate let-item="item">
<StackLayout orientation="vertical">
<Label [text]="item.firstname"></Label>
<Label [text]="item.lastname"></Label>
</StackLayout>
</ng-template>
</RadListView>
</GridLayout>
I have created a playground for you here. When you are using a GridLayout, you need to assign row to RadListView.
<GridLayout rows="auto, auto'>
<RadListView row="0" [items]=" dataItems">
<ng-template tkListItemTemplate let-item="item">
<StackLayout orientation="vertical">
<Label [text]="item.firstname"></Label>
<Label [text]="item.lastname"></Label>
</StackLayout>
</ng-template>
</RadListView>
<RadListView row="1" [items]="dataItems">
<ng-template tkListItemTemplate let-item="item">
<StackLayout orientation="vertical">
<Label [text]="item.firstname"></Label>
<Label [text]="item.lastname"></Label>
</StackLayout>
</ng-template>
</RadListView>
</GridLayout>

Nativescript Layout Incorrect for Out of View Content in iOS

I have been battling this issue for the past couple of days. Hopefully someone can see what I'm missing.
I have a view that displays a list nested inside a parent list. The parent list has header detail. For the content that is displayed initially, everything is correct. For the header information that is out of view, when scrolled into view, it is incorrect.
<ScrollView>
<FlexboxLayout flexDirection="column">
<StackLayout *ngFor="let property of properties">
<FlexboxLayout flexDirection="row">
<FlexboxLayout flexDirection="column" alignContent="center" style="background-color: red;">
<Label [text]="property.addressLine1"></Label>
<Label [text]="formatCityStatePostalCode(property)"></Label>
</FlexboxLayout>
</FlexboxLayout>
<StackLayout *ngFor="let action of property.actions">
<Label style="padding-bottom: 30" [text]="action.title"></Label>
</StackLayout>
</StackLayout>
</FlexboxLayout>
I have tried using StackLayouts, GridLayouts and FlexboxLayouts and all produce the exact same result.
Display when both headers are in the initial view
You will notice the headers are identical.
When the second header is off screen, it looks like this.
Off screen header displayed
I have experienced this before with views that are offscreen getting stretched to match their bottom with the bottom of the scrollview. If you colour the backgrounds of all of those labels (with 20% opacity), you'll notice the overlapping.
This can be resolved (temporarily?) by setting iosOverflowSafeArea="false" on generated elements within a ScrollView.
<ScrollView>
<FlexboxLayout flexDirection="column">
<StackLayout *ngFor="let property of properties" iosOverflowSafeArea="false">
<FlexboxLayout flexDirection="row">
<FlexboxLayout flexDirection="column" alignContent="center" style="background-color: red;">
<Label [text]="property.addressLine1"></Label>
<Label [text]="formatCityStatePostalCode(property)"></Label>
</FlexboxLayout>
</FlexboxLayout>
<StackLayout *ngFor="let action of property.actions" iosOverflowSafeArea="false">
<Label style="padding-bottom: 30" [text]="action.title"></Label>
</StackLayout>
</StackLayout>
</FlexboxLayout>
</ScrollView>
Discussion was here: https://nativescriptcommunity.slack.com/archives/C0L9EEURY/p1539955914000100

Xamarin.Forms (iOS Project): ListView Separator shows in all screen

I have a ListView with the default SeparatorVisibility. My Android project shows the Separator if there are elements in the ItemsSource and stops showing it below the last element. It's the result I want for my iOS project.
However, in my iOS project the screen is full of Separators no matter how many elements I have, even if I have no elements or only one, the Separators still being there.
Could someone give me a reason and how to fix it please? Thanks.
I think you can take a look to this post
these are some tips
First disable the default separator, this is done by adding following property to the ListView XAML
SeparatorColor="Transparent"
After this, wrap the complete ViewCell content inside a double StackLayout! I know this sounds like overkill but this way you’ll not run into any BoxView issues regarding margins inside the ViewCell… or other stuff.
The first StackLayout should have a BackgroundColor set to the colour you want your separator to be, the second StackLayout should have the same BackgroundColor as the rest of the container it is in… in our example the page and that is set to white. Be sure to also add a Margin to the bottom of this second StackLayout because that will represent the thickness of our separator!
I think you can play with this "Margin"... when your data is empty, remove the margin so you should not have the separator
<ListView x:Name="SeparatorListView"
SeparatorColor="Transparent"
ItemsSource="{Binding Persons}"
Margin="0,20,0,0"
RowHeight="60"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
BackgroundColor="White"
Grid.Row="1">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell IsEnabled="false">
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
BackgroundColor="Black">
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
BackgroundColor="White"
Margin="0,0,0,0.4">
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand" Spacing="0">
<Label Text="{Binding FullName}" TextColor="Maroon" VerticalOptions="CenterAndExpand" Margin="20,0,20,0" />
<Label Text="{Binding Profession}" TextColor="Maroon" VerticalOptions="CenterAndExpand" Margin="20,0,20,0" />
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
With that in place, you’ll get the same visual result as the preview image at the top right of this blog post.
As a bonus, you could omit one of the StackLayouts IF your page has a background color other than white. Because if this is the case, you can use that color as the separator color by playing with transparency inside the ListView.
Example of this, note will only work if the page itself also has a BackgroundColor set to Olive!
<ListView x:Name="SeparatorListView"
SeparatorColor="Transparent"
ItemsSource="{Binding Persons}"
Margin="0,20,0,0"
RowHeight="60"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
BackgroundColor="Olive"
Grid.Row="1">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell IsEnabled="false">
<StackLayout BackgroundColor="#f4eac3"
Padding="0,5,0,5"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout BackgroundColor="Transparent"
HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand"
Spacing="0"
Margin="20,0,20,0">
<Label Text="{Binding FullName}" TextColor="Maroon" VerticalOptions="CenterAndExpand" />
<Label Text="{Binding Profession}" TextColor="Maroon" VerticalOptions="CenterAndExpand" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer (typeof(ListView), typeof(CustomListViewRenderer))]
namespace yourNamespace
{
public class CustomListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var listView = Control as UITableView;
listView.SeparatorInset = UIEdgeInsets.Zero;
}
}
}
}
That is due to the implementation of the ListView on the iOS side. It renders as a UITableView which (in the case of the ListView) always takes the entire height and shows empty items. To change this type of behavior you could set the ListView height dynamically in the code-behind for your page. Another option is taking a look at Xamarin's Evolve sample app which has a few pages where it uses a CardView combined with a ListView to make it appear as it does on Android.

WPF: Customized control focus management

I was tying to set focus on a TextBox used to build a customized "Numeric Up Down" control.
This is the custom "Numeric Up Down"
<Style x:Key="SpinButton" TargetType="Slider" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Slider">
<Border Grid.RowSpan="3" BorderThickness="1,1,1,1" BorderBrush="WhiteSmoke">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height=".5*"/>
<RowDefinition Height=".5*"/>
</Grid.RowDefinitions>
<TextBox Name="textBoxNumericUpDown" Grid.RowSpan="3" Margin="2,0,0,0" Text="{Binding Value,Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" FontSize="20" VerticalAlignment="Center" Background=" {x:Null}" Foreground="White" BorderThickness="0" CaretBrush="White" IsTabStop="True"/>
<RepeatButton Grid.Column="1" Grid.Row="0" Margin="0,0,-5,0" Command="Slider.IncreaseLarge" Style="{StaticResource TimeRepeatButtonStyle}" Content="+" Background="{x:Null}" BorderBrush="White" Foreground="White" FontSize="18" Cursor="Hand" Width="44" Height="44" IsTabStop="False"/>
<RepeatButton Grid.Column="1" Grid.Row="1" Command="Slider.DecreaseLarge" Margin="0,1,-5,0" Style="{StaticResource TimeRepeatButtonStyle}" Content="-" Background="{x:Null}" BorderBrush="White" Foreground="White" FontSize="18" Cursor="Hand" Width="44" Height="44" IsTabStop="False"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage
<Slider PreviewTextInput="textBoxTimeMinute_PreviewTextInput" Name="textBoxTimeMinute" Width="100" Style="{StaticResource SpinButton}" Maximum="999" Margin="8" HorizontalAlignment="Left" Minimum="0" IsTabStop="True" TabIndex="0"/>
<Label Grid.Column="1" Content=":" VerticalAlignment="Center" IsTabStop="False"/>
<Slider PreviewTextInput="textBoxTimeSecond_PreviewTextInput" Grid.Column="2" Name="textBoxTimeSecond" Width="100" Style="{StaticResource SpinButton}" Maximum="60" Margin="8" HorizontalAlignment="Left" Minimum="-1" IsTabStop="True" TabIndex="1"/>
Wanted result (Automatic focus on the first textbox if no number inside)
Wanted result (Automatic focus on the first textbox if number inside, number is selected)
I tried different ways to make it select the correct Control, but it always selects the outer layer. In my case The Grid/Border.
Only after I press TAB I can have it select the correct TextBox Control.
Is there any way in WPF I can achieve the focus on textbox without pressing TAB?
Solved:
If you want to focus a TextBox inside a ControlTemplate you have to set up a trigger on the "IsFocused" property.
When it becomes true then you have to set the FocusManager.FocusedElement field to the name of the TextBox (or other control) you want to focus.
Below is the code I used to achieve this:
<ControlTemplate TargetType="Slider">
<TextBox x:Name="TextBoxToFocus"/> //The textbox(or any other element) I want to set focus to. It is very important to have it named, otherwise I won't know which control to focus.
<ControlTemplate.Triggers> //The trigger
<Trigger Property = "IsFocused" Value = "True" /> //The property the trigger is watching
<Setter TargetName="TextBoxToFocus" Property="FocusManager.FocusedElement" Value="{Binding ElementName=NumericUpDown}"/> //Setter when the trigger executes
</Trigger>
</ControlTelmplate.Triggers>

Listbox of buttons and relative source

I have a listbox bound to a view model property called Choices. Each choice has a label and an index. I need to bind the buttons in the list to a command on the same view model. So far Ive figured out this much:
<ListBox Grid.Row="1" ItemsSource="{Binding Choices}" SelectedIndex="{Binding SelectedChoice, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Margin="1">
<Button Content="{Binding Caption}" Height="24" HorizontalAlignment="Stretch"
Command="{Binding RelativeSource={RelativeSource ???},
Path=SelectChoice}" CommandParameter="{Binding}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I cant figure out what RelativeSource to use for the command and I'm not sure the CommandParameter is correct.
This seems a really simple thing to do but it's obviously too simple for my poor old brain. Can anyone help please?
Thanks
Sorted:
<ItemsControl Grid.Row="1" ItemsSource="{Binding Choices}" >
<ItemsControl.ItemsPanel >
<ItemsPanelTemplate >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Caption}" Height="24" HorizontalAlignment="Stretch" Margin="0,0,4,0"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.SelectChoice}"
CommandParameter="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Resources