[RenderWith(typeof(RefreshViewRenderer))]
#endif
internal class _RefreshViewRenderer { }
+#if !TIZEN4_0
+ [RenderWith(typeof(SwipeViewRenderer))]
+#endif
+ internal class _SwipeViewRenderer { }
}
_abortStressTest = false;
int.TryParse(_stressTestItertionEntry.Text, out _stressTestIterationCount);
-
+
#if __UWP__
Windows.System.Threading.ThreadPool.RunAsync(delegate { runStressTest(); });
#else
using Xamarin.Forms.Controls.Issues;
using Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselViewGalleries;
using Xamarin.Forms.Controls.GalleryPages.RefreshViewGalleries;
+using Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries;
namespace Xamarin.Forms.Controls
{
new GalleryPageFactory(() => new SliderCoreGalleryPage(), "Slider Gallery"),
new GalleryPageFactory(() => new StepperCoreGalleryPage(), "Stepper Gallery"),
new GalleryPageFactory(() => new SwitchCoreGalleryPage(), "Switch Gallery"),
+ new GalleryPageFactory(() => new SwipeViewCoreGalleryPage(), "SwipeView Core Gallery"),
+ new GalleryPageFactory(() => new SwipeViewGallery(), "SwipeView Gallery"),
new GalleryPageFactory(() => new TableViewCoreGalleryPage(), "TableView Gallery"),
new GalleryPageFactory(() => new TimePickerCoreGalleryPage(), "TimePicker Gallery"),
new GalleryPageFactory(() => new VisualGallery(), "Visual Gallery"),
--- /dev/null
+using Xamarin.Forms.CustomAttributes;
+
+namespace Xamarin.Forms.Controls
+{
+ internal class SwipeViewCoreGalleryPage : CoreGalleryPage<SwipeView>
+ {
+ protected override bool SupportsFocus
+ {
+ get { return false; }
+ }
+
+ protected override bool SupportsScroll
+ {
+ get { return false; }
+ }
+
+ protected override void InitializeElement(SwipeView element)
+ {
+ element.HeightRequest = 60;
+ element.BackgroundColor = Color.LightGray;
+ element.Content = GetSwipeContent(SwipeDirection.Left);
+ element.LeftItems = GetRevealSwipeItems();
+ element.RightItems = GetExecuteSwipeItems();
+ }
+
+ protected override void Build(StackLayout stackLayout)
+ {
+ base.Build(stackLayout);
+
+ var rightItemsContainer = new ValueViewContainer<SwipeView>(Test.SwipeView.RightItems, new SwipeView { RightItems = GetRevealSwipeItems(), Content = GetSwipeContent(SwipeDirection.Right), HeightRequest = 60, BackgroundColor = Color.LightPink }, "RightItems", value => value.ToString());
+ var topItemsContainer = new ValueViewContainer<SwipeView>(Test.SwipeView.TopItems, new SwipeView { TopItems = GetRevealSwipeItems(), Content = GetSwipeContent(SwipeDirection.Up), HeightRequest = 60, BackgroundColor = Color.LightSkyBlue }, "TopItems", value => value.ToString());
+ var bottomItemsContainer = new ValueViewContainer<SwipeView>(Test.SwipeView.BottomItems, new SwipeView { BottomItems = GetRevealSwipeItems(), Content = GetSwipeContent(SwipeDirection.Down), HeightRequest = 60, BackgroundColor = Color.LightGray }, "BottomItems", value => value.ToString());
+
+ Add(rightItemsContainer);
+ Add(topItemsContainer);
+ Add(bottomItemsContainer);
+ }
+
+ internal SwipeItems GetRevealSwipeItems()
+ {
+ var addSwipeItem = new SwipeItem { BackgroundColor = Color.Green, Text = "Add", IconImageSource = "coffee.png" };
+ addSwipeItem.Invoked += (sender, e) =>
+ {
+ DisplayAlert("SwipeView", "Add Invoked", "OK");
+ };
+
+ var modifySwipeItem = new SwipeItem { BackgroundColor = Color.Orange, Text = "Modify", IconImageSource = "calculator.png" };
+
+ modifySwipeItem.Invoked += (sender, e) =>
+ {
+ DisplayAlert("SwipeView", "Modify Invoked", "OK");
+ };
+
+ var swipeItems = new SwipeItems
+ {
+ addSwipeItem,
+ modifySwipeItem
+ };
+
+ swipeItems.Mode = SwipeMode.Reveal;
+
+ return swipeItems;
+ }
+
+ internal SwipeItems GetExecuteSwipeItems()
+ {
+ var deleteSwipeItem = new SwipeItem { BackgroundColor = Color.Red, Text = "Delete", IconImageSource = "coffee.png" };
+
+ deleteSwipeItem.Invoked += (sender, e) =>
+ {
+ DisplayAlert("SwipeView", "Delete Invoked", "OK");
+ };
+
+ var swipeItems = new SwipeItems
+ {
+ deleteSwipeItem
+ };
+
+ swipeItems.Mode = SwipeMode.Execute;
+
+ return swipeItems;
+ }
+
+ internal Grid GetSwipeContent(SwipeDirection swipeDirection)
+ {
+ var content = new Grid
+ {
+ BackgroundColor = Color.LightGoldenrodYellow
+ };
+
+ var info = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ switch (swipeDirection)
+ {
+ case SwipeDirection.Down:
+ info.Text = "Swipe to the Top";
+ break;
+ case SwipeDirection.Left:
+ info.Text = "Swipe to the Right";
+ break;
+ case SwipeDirection.Right:
+ info.Text = "Swipe to the Left";
+ break;
+ case SwipeDirection.Up:
+ info.Text = "Swipe to the Bottom";
+ break;
+ }
+
+ content.Children.Add(info);
+
+ return content;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Linq;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class AddRemoveSwipeItemsGallery : ContentPage
+ {
+ public AddRemoveSwipeItemsGallery()
+ {
+ Title = "Add/Remove SwipeItems Gallery";
+
+ var swipeLayout = new StackLayout
+ {
+ Margin = new Thickness(12)
+ };
+
+ var buttonsLayout = new StackLayout
+ {
+ Orientation = StackOrientation.Horizontal
+ };
+
+ var addButton = new Button
+ {
+ Text = "Add SwipeItem"
+ };
+
+ buttonsLayout.Children.Add(addButton);
+
+ var removeButton = new Button
+ {
+ Text = "Remove SwipeItem"
+ };
+
+ buttonsLayout.Children.Add(removeButton);
+
+ var swipeItemIconLabel = new Label
+ {
+ FontSize = 10,
+ Text = "Choose SwipeItem Icon:"
+ };
+
+ var deleteSwipeItem = new SwipeItem
+ {
+ BackgroundColor = Color.Orange,
+ IconImageSource = "calculator.png",
+ Text = "SwipeItem1"
+ };
+
+ deleteSwipeItem.Invoked += (sender, e) => { DisplayAlert("SwipeView", "Delete Invoked", "Ok"); };
+
+ var leftSwipeItems = new SwipeItems
+ {
+ deleteSwipeItem
+ };
+
+ leftSwipeItems.Mode = SwipeMode.Reveal;
+
+ var swipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var swipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Right"
+ };
+
+ swipeContent.Children.Add(swipeLabel);
+
+ var swipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ LeftItems = leftSwipeItems,
+ Content = swipeContent
+ };
+
+ swipeLayout.Children.Add(buttonsLayout);
+ swipeLayout.Children.Add(swipeView);
+
+ Content = swipeLayout;
+
+ addButton.Clicked += (sender, e) =>
+ {
+ swipeView.Close();
+
+ var swipeItemsCount = swipeView.LeftItems.Count;
+ var random = new Random();
+
+ var newSwipeItem = new SwipeItem
+ {
+ BackgroundColor = Color.FromRgb(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)),
+ IconImageSource = "calculator.png",
+ Text = $"SwipeItem{swipeItemsCount + 1}"
+ };
+
+ swipeView.LeftItems.Add(newSwipeItem);
+ };
+
+ removeButton.Clicked += (sender, e) =>
+ {
+ swipeView.Close();
+
+ var swipeItemsCount = swipeView.LeftItems.Count;
+
+ if(swipeItemsCount > 0)
+ {
+ var lastSwipeItem = swipeView.LeftItems.LastOrDefault();
+ swipeView.LeftItems.Remove(lastSwipeItem);
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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="Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries.BasicSwipeGallery"
+ Title="SwipeView">
+ <ContentPage.Content>
+ <StackLayout>
+ <SwipeView
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Margin="12">
+ <SwipeView.BottomItems>
+ <SwipeItems
+ Mode="Execute">
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Invoked="OnInvoked"/>
+ </SwipeItems>
+ </SwipeView.BottomItems>
+ <SwipeView.Content>
+ <Grid
+ HeightRequest="60"
+ WidthRequest="300"
+ BackgroundColor="LightGray">
+ <Label
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Text="Swipe Up (Execute)"/>
+ </Grid>
+ </SwipeView.Content>
+ </SwipeView>
+ <SwipeView
+ HeightRequest="60"
+ WidthRequest="300"
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Margin="12">
+ <SwipeView.TopItems>
+ <SwipeItems
+ Mode="Reveal">
+ <SwipeItem
+ Text="View"
+ Icon="books.png"
+ BackgroundColor="Green"/>
+ <SwipeItem
+ Text="Edit"
+ Icon="calculator.png"
+ BackgroundColor="Orange"/>
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Invoked="OnInvoked"/>
+ </SwipeItems>
+ </SwipeView.TopItems>
+ <SwipeView.Content>
+ <Grid
+ BackgroundColor="LightGray">
+ <Label
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Text="Swipe Down (Reveal)"/>
+ </Grid>
+ </SwipeView.Content>
+ </SwipeView>
+ <SwipeView
+ HeightRequest="60"
+ WidthRequest="300"
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Margin="12">
+ <SwipeView.LeftItems>
+ <SwipeItems
+ Mode="Reveal">
+ <SwipeItem
+ Text="View"
+ Icon="books.png"
+ BackgroundColor="Green"/>
+ <SwipeItem
+ Text="Edit"
+ Icon="calculator.png"
+ BackgroundColor="Orange"/>
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Invoked="OnInvoked"/>
+ </SwipeItems>
+ </SwipeView.LeftItems>
+ <SwipeView.Content>
+ <Grid
+ BackgroundColor="LightGray">
+ <Label
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Text="Swipe Right (Reveal)"/>
+ </Grid>
+ </SwipeView.Content>
+ </SwipeView>
+ <SwipeView
+ HeightRequest="60"
+ WidthRequest="300"
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Margin="12">
+ <SwipeView.RightItems>
+ <SwipeItems
+ Mode="Execute">
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Invoked="OnInvoked"/>
+ </SwipeItems>
+ </SwipeView.RightItems>
+ <SwipeView.Content>
+ <Grid
+ BackgroundColor="LightGray">
+ <Label
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Text="Swipe Left (Execute)"/>
+ </Grid>
+ </SwipeView.Content>
+ </SwipeView>
+ </StackLayout>
+ </ContentPage.Content>
+</ContentPage>
\ No newline at end of file
--- /dev/null
+using System;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public partial class BasicSwipeGallery : ContentPage
+ {
+ public BasicSwipeGallery()
+ {
+ InitializeComponent();
+ }
+
+ private void OnInvoked(object sender, EventArgs e)
+ {
+ DisplayAlert("SwipeView", "Delete Invoked", "OK");
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ public class CloseSwipeGallery : ContentPage
+ {
+ public CloseSwipeGallery()
+ {
+ Title = "Close SwipeView Gallery";
+
+ var swipeLayout = new StackLayout
+ {
+ Margin = new Thickness(12)
+ };
+
+ var closeButton = new Button
+ {
+ Text = "Close SwipeView"
+ };
+
+ swipeLayout.Children.Add(closeButton);
+
+ var swipeItem = new SwipeItem
+ {
+ BackgroundColor = Color.Red,
+ IconImageSource = "calculator.png",
+ Text = "File"
+ };
+
+ swipeItem.Invoked += (sender, e) => { DisplayAlert("SwipeView", "File Invoked", "Ok"); };
+
+ var swipeItems = new SwipeItems { swipeItem };
+
+ swipeItems.Mode = SwipeMode.Reveal;
+
+ var swipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var fileSwipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Right (File)"
+ };
+
+ swipeContent.Children.Add(fileSwipeLabel);
+
+ var swipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ LeftItems = swipeItems,
+ Content = swipeContent
+ };
+
+ swipeLayout.Children.Add(swipeView);
+
+ Content = swipeLayout;
+
+ closeButton.Clicked += (sender, e) =>
+ {
+ swipeView.Close();
+ };
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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="Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries.CustomSizeSwipeViewGallery"
+ Title="Custom Size SwipeView">
+ <ContentPage.Content>
+ <Grid>
+ <SwipeView>
+ <SwipeView.LeftItems>
+ <SwipeItemView>
+ <Grid
+ BackgroundColor="LightPink"
+ WidthRequest="200">
+ <Label
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Text="This is the LefItems Content"
+ Margin="12"/>
+ </Grid>
+ </SwipeItemView>
+ </SwipeView.LeftItems>
+ <SwipeView.RightItems>
+ <SwipeItem
+ IconImageSource="calculator.png"
+ Text="Test"
+ BackgroundColor="Red" />
+ <SwipeItemView>
+ <Grid
+ BackgroundColor="LightSteelBlue"
+ WidthRequest="200">
+ <Label
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Text="This is the RightItems Content"
+ Margin="12"/>
+ </Grid>
+ </SwipeItemView>
+ </SwipeView.RightItems>
+ <SwipeView.TopItems>
+ <SwipeItemView>
+ <Grid
+ BackgroundColor="LightSkyBlue"
+ HeightRequest="100">
+ <Label
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Text="This is the TopItems Content"
+ Margin="12"/>
+ </Grid>
+ </SwipeItemView>
+ </SwipeView.TopItems>
+ <Grid
+ BackgroundColor="LightGreen">
+ <Label
+ HorizontalOptions="Center"
+ VerticalOptions="Center"
+ Text="This is the Content"
+ Margin="12"/>
+ </Grid>
+ </SwipeView>
+ </Grid>
+ </ContentPage.Content>
+</ContentPage>
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public partial class CustomSizeSwipeViewGallery : ContentPage
+ {
+ public CustomSizeSwipeViewGallery()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class CustomSwipeItemGallery : ContentPage
+ {
+ public CustomSwipeItemGallery()
+ {
+ Title = "CustomSwipeItem Galleries";
+ var layout = new StackLayout
+ {
+ Children =
+ {
+ GalleryBuilder.NavButton("Customize SwipeItem Gallery", () => new CustomizeSwipeItemGallery(), Navigation)
+ }
+ };
+
+ if (Device.RuntimePlatform != Device.UWP)
+ {
+ layout.Children.Add(GalleryBuilder.NavButton("SwipeItemView Gallery", () => new CustomSwipeItemViewGallery(), Navigation));
+ layout.Children.Add(GalleryBuilder.NavButton("CustomSwipeItem Size Gallery", () => new CustomSizeSwipeViewGallery(), Navigation));
+ }
+
+ Content = layout;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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="Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries.CustomSwipeItemViewGallery"
+ Title="CustomSwipeItem">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+
+ <Color x:Key="BackgroundColor">#2E249E</Color>
+ <Color x:Key="SwipeItemBackgroundColor">#FE744D</Color>
+ <Color x:Key="TitleColor">#55A1FA</Color>
+ <Color x:Key="SubTitleColor">#FFFFFF</Color>
+
+ <Style x:Key="TitleStyle" TargetType="Label">
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="TextColor" Value="{StaticResource TitleColor}" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="DateStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="{StaticResource SubTitleColor}" />
+ <Setter Property="FontSize" Value="18" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="SwipeItemTextStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="White" />
+ <Setter Property="FontAttributes" Value="Bold" />
+ <Setter Property="FontSize" Value="12" />
+ <Setter Property="HorizontalOptions" Value="Center" />
+ <Setter Property="VerticalOptions" Value="Center" />
+ </Style>
+
+ <DataTemplate x:Key="MessageTemplate">
+ <SwipeView
+ HeightRequest="80">
+ <SwipeView.RightItems>
+ <SwipeItems
+ Mode="Reveal">
+ <SwipeItemView
+ Command="{Binding Source={x:Reference CustomSwipeItemCollectionView}, Path=BindingContext.FavouriteCommand}">
+ <Grid
+ WidthRequest="100">
+ <BoxView
+ BackgroundColor="{StaticResource SwipeItemBackgroundColor}"
+ CornerRadius="0, 6, 0, 6" />
+ <Label
+ Text="Favourite"
+ Style="{StaticResource SwipeItemTextStyle}"/>
+ </Grid>
+ </SwipeItemView>
+ </SwipeItems>
+ </SwipeView.RightItems>
+ <SwipeView.Content>
+ <Frame
+ BackgroundColor="{StaticResource BackgroundColor}"
+ CornerRadius="6"
+ HasShadow="False"
+ Padding="12">
+ <Grid
+ VerticalOptions="Center"
+ RowSpacing="0">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <Label
+ Grid.Row="0"
+ Text="{Binding Title}"
+ Style="{StaticResource TitleStyle}"/>
+ <Label
+ Grid.Row="1"
+ Text="{Binding Date}"
+ Style="{StaticResource DateStyle}"/>
+ </Grid>
+ </Frame>
+ </SwipeView.Content>
+ </SwipeView>
+ </DataTemplate>
+
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <Grid>
+ <CollectionView
+ x:Name="CustomSwipeItemCollectionView"
+ ItemsSource="{Binding Messages}"
+ ItemTemplate="{StaticResource MessageTemplate}"
+ Margin="12, 12, 12, 0">
+ <CollectionView.ItemsLayout>
+ <LinearItemsLayout
+ Orientation="Vertical"
+ ItemSpacing="12"/>
+ </CollectionView.ItemsLayout>
+ </CollectionView>
+ </Grid>
+ </ContentPage.Content>
+</ContentPage>
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public partial class CustomSwipeItemViewGallery : ContentPage
+ {
+ public CustomSwipeItemViewGallery()
+ {
+ InitializeComponent();
+ BindingContext = new SwipeViewGalleryViewModel();
+
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "favourite", sender => { DisplayAlert("SwipeView", "Favourite", "Ok"); });
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "delete", sender => { DisplayAlert("SwipeView", "Delete", "Ok"); });
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Generic;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class CustomizeSwipeItemGallery : ContentPage
+ {
+ public CustomizeSwipeItemGallery()
+ {
+ Title = "Customize SwipeItem Gallery";
+
+ var swipeLayout = new StackLayout
+ {
+ Margin = new Thickness(12)
+ };
+
+ var swipeItemTextLabel = new Label
+ {
+ FontSize = 10,
+ Text = "SwipeItem Text:"
+ };
+
+ swipeLayout.Children.Add(swipeItemTextLabel);
+
+ var swipeItemTextEntry = new Entry
+ {
+ Text = "Delete",
+ Placeholder = "SwipeItem Text"
+ };
+
+ swipeLayout.Children.Add(swipeItemTextEntry);
+
+ var swipeItemBackgroundColorLabel = new Label
+ {
+ FontSize = 10,
+ Text = "Choose SwipeItem BackgroundColor:"
+ };
+
+ swipeLayout.Children.Add(swipeItemBackgroundColorLabel);
+
+ var swipeItemBackgroundColorPicker = new Picker();
+ var colors = new List<string> { "#FFFFFF", "#FF0000", "#00FF00", "#0000FF", "#000000" };
+ swipeItemBackgroundColorPicker.ItemsSource = colors;
+ swipeItemBackgroundColorPicker.SelectedItem = colors[1];
+
+ swipeLayout.Children.Add(swipeItemBackgroundColorPicker);
+
+
+ var swipeItemIconLabel = new Label
+ {
+ FontSize = 10,
+ Text = "Choose SwipeItem Icon:"
+ };
+
+ swipeLayout.Children.Add(swipeItemIconLabel);
+
+ var swipeItemIconPicker = new Picker();
+ var icons = new List<string> { "bank.png", "calculator.png", "coffee.png" };
+ swipeItemIconPicker.ItemsSource = icons;
+ swipeItemIconPicker.SelectedItem = icons[1];
+
+ swipeLayout.Children.Add(swipeItemIconPicker);
+
+ var deleteSwipeItem = new SwipeItem
+ {
+ BackgroundColor = Color.FromHex(colors[swipeItemBackgroundColorPicker.SelectedIndex]),
+ IconImageSource = swipeItemIconPicker.SelectedItem.ToString(),
+ Text = swipeItemTextEntry.Text
+ };
+
+ deleteSwipeItem.Invoked += (sender, e) => { DisplayAlert("SwipeView", "Delete Invoked", "Ok"); };
+
+ var leftSwipeItems = new SwipeItems
+ {
+ deleteSwipeItem
+ };
+
+ leftSwipeItems.Mode = SwipeMode.Execute;
+
+ var swipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var swipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Right"
+ };
+
+ swipeContent.Children.Add(swipeLabel);
+
+ var swipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ LeftItems = leftSwipeItems,
+ Content = swipeContent
+ };
+
+ swipeLayout.Children.Add(swipeView);
+
+ swipeItemTextEntry.TextChanged += (sender, e) =>
+ {
+ deleteSwipeItem.Text = swipeItemTextEntry.Text;
+ };
+
+ swipeItemBackgroundColorPicker.SelectedIndexChanged += (sender, e) =>
+ {
+ deleteSwipeItem.BackgroundColor = Color.FromHex(colors[swipeItemBackgroundColorPicker.SelectedIndex]);
+ };
+
+ swipeItemIconPicker.SelectedIndexChanged += (sender, e) =>
+ {
+ deleteSwipeItem.IconImageSource = swipeItemIconPicker.SelectedItem.ToString();
+ };
+
+ Content = swipeLayout;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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="Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries.ResourceSwipeItemsGallery"
+ Title="SwipeItems from Resource">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+
+ <Style x:Key="TitleStyle" TargetType="Label">
+ <Setter Property="FontSize" Value="14" />
+ <Setter Property="TextColor" Value="Black" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="DateStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="SubTitleStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="12" />
+ <Setter Property="Margin" Value="6, 0" />
+ </Style>
+
+ <SwipeItems x:Key="FavouriteSwipeItems">
+ <SwipeItem
+ Text="Favourite"
+ Icon="calculator.png"
+ BackgroundColor="Yellow"
+ Command="{Binding Source={x:Reference SwipeCollectionView}, Path=BindingContext.FavouriteCommand}"/>
+ </SwipeItems>
+
+ <SwipeItems
+ x:Key="DeleteSwipeItems"
+ Mode="Execute">
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Command="{Binding Source={x:Reference SwipeCollectionView}, Path=BindingContext.DeleteCommand}"/>
+ </SwipeItems>
+
+ <DataTemplate x:Key="MessageTemplate">
+ <SwipeView
+ HeightRequest="80"
+ LeftItems="{StaticResource FavouriteSwipeItems}"
+ RightItems="{StaticResource DeleteSwipeItems}">
+ <SwipeView.Content>
+ <Grid
+ BackgroundColor="White"
+ RowSpacing="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Label
+ Grid.Column="0"
+ Grid.Row="0"
+ Text="{Binding Title}"
+ Style="{StaticResource TitleStyle}"/>
+ <Label
+ Grid.Column="1"
+ Grid.Row="0"
+ Text="{Binding Date}"
+ Style="{StaticResource DateStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="1"
+ Text="{Binding SubTitle}"
+ Style="{StaticResource SubTitleStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="2"
+ Text="{Binding Description}"
+ Style="{StaticResource SubTitleStyle}"/>
+ </Grid>
+ </SwipeView.Content>
+ </SwipeView>
+ </DataTemplate>
+
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <Grid>
+ <CollectionView
+ x:Name="SwipeCollectionView"
+ ItemsSource="{Binding Messages}"
+ ItemTemplate="{StaticResource MessageTemplate}" />
+ </Grid>
+ </ContentPage.Content>
+</ContentPage>
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public partial class ResourceSwipeItemsGallery : ContentPage
+ {
+ public ResourceSwipeItemsGallery()
+ {
+ InitializeComponent();
+ BindingContext = new SwipeViewGalleryViewModel();
+
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "favourite", sender => { DisplayAlert("SwipeView", "Favourite", "Ok"); });
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "delete", sender => { DisplayAlert("SwipeView", "Delete", "Ok"); });
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Linq;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class SwipeBehaviorOnInvokedGallery : ContentPage
+ {
+ public SwipeBehaviorOnInvokedGallery()
+ {
+ Title = "SwipeBehaviorOnInvoked Gallery";
+
+ var swipeLayout = new StackLayout
+ {
+ Margin = new Thickness(12)
+ };
+
+ var swipeBehaviorOnInvokedLabel = new Label
+ {
+ FontSize = 10,
+ Text = "SwipeBehaviorOnInvoked:"
+ };
+
+ swipeLayout.Children.Add(swipeBehaviorOnInvokedLabel);
+
+ var swipeBehaviorOnInvokedItems = Enum.GetNames(typeof(SwipeBehaviorOnInvoked)).Select(s => s).ToList();
+
+ var swipeBehaviorOnInvokedPicker = new Picker
+ {
+ ItemsSource = swipeBehaviorOnInvokedItems,
+ SelectedIndex = 0
+ };
+
+ swipeLayout.Children.Add(swipeBehaviorOnInvokedPicker);
+
+ var deleteSwipeItem = new SwipeItem { BackgroundColor = Color.Red, Text = "Delete", IconImageSource = "calculator.png" };
+ deleteSwipeItem.Invoked += (sender, e) => { DisplayAlert("SwipeView", "Delete Invoked", "Ok"); };
+
+ var leftSwipeItems = new SwipeItems
+ {
+ deleteSwipeItem
+ };
+
+ leftSwipeItems.Mode = SwipeMode.Reveal;
+ leftSwipeItems.SwipeBehaviorOnInvoked = SwipeBehaviorOnInvoked.Auto;
+
+ var swipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var swipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Right (and tap the SwipeItem)"
+ };
+
+ swipeContent.Children.Add(swipeLabel);
+
+ var swipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ LeftItems = leftSwipeItems,
+ Content = swipeContent
+ };
+
+ swipeLayout.Children.Add(swipeView);
+
+ swipeBehaviorOnInvokedPicker.SelectedIndexChanged += (sender, e) =>
+ {
+ Enum.TryParse(swipeBehaviorOnInvokedPicker.SelectedItem.ToString(), out SwipeBehaviorOnInvoked swipeBehaviorOnInvoked);
+ leftSwipeItems.SwipeBehaviorOnInvoked = swipeBehaviorOnInvoked;
+ };
+
+ Content = swipeLayout;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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="Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries.SwipeBindableLayoutGallery"
+ Title="Swipe BindableLayout">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+
+ <Style x:Key="TitleStyle" TargetType="Label">
+ <Setter Property="FontSize" Value="14" />
+ <Setter Property="TextColor" Value="Black" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="DateStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="SubTitleStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="12" />
+ <Setter Property="Margin" Value="6, 0" />
+ </Style>
+
+ <DataTemplate x:Key="MessageTemplate">
+ <SwipeView
+ HeightRequest="80">
+ <SwipeView.LeftItems>
+ <SwipeItems>
+ <SwipeItem
+ Text="Favourite"
+ Icon="calculator.png"
+ BackgroundColor="Yellow"
+ Command="{Binding Source={x:Reference SwipeStackLayout}, Path=BindingContext.FavouriteCommand}"/>
+ </SwipeItems>
+ </SwipeView.LeftItems>
+ <SwipeView.RightItems>
+ <SwipeItems
+ Mode="Execute">
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Command="{Binding Source={x:Reference SwipeStackLayout}, Path=BindingContext.DeleteCommand}"/>
+ </SwipeItems>
+ </SwipeView.RightItems>
+ <SwipeView.Content>
+ <Grid
+ BackgroundColor="White"
+ RowSpacing="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Label
+ Grid.Column="0"
+ Grid.Row="0"
+ Text="{Binding Title}"
+ Style="{StaticResource TitleStyle}"/>
+ <Label
+ Grid.Column="1"
+ Grid.Row="0"
+ Text="{Binding Date}"
+ Style="{StaticResource DateStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="1"
+ Text="{Binding SubTitle}"
+ Style="{StaticResource SubTitleStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="2"
+ Text="{Binding Description}"
+ Style="{StaticResource SubTitleStyle}"/>
+ </Grid>
+ </SwipeView.Content>
+ </SwipeView>
+ </DataTemplate>
+
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <ScrollView
+ HorizontalScrollBarVisibility="Never">
+ <StackLayout
+ x:Name="SwipeStackLayout"
+ BindableLayout.ItemsSource="{Binding Messages}"
+ BindableLayout.ItemTemplate="{StaticResource MessageTemplate}"/>
+ </ScrollView>
+ </ContentPage.Content>
+</ContentPage>
\ No newline at end of file
--- /dev/null
+using System.Collections.ObjectModel;
+using System.Windows.Input;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public partial class SwipeBindableLayoutGallery : ContentPage
+ {
+ public SwipeBindableLayoutGallery()
+ {
+ InitializeComponent();
+ BindingContext = new SwipeViewGalleryViewModel();
+
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "favourite", sender => { DisplayAlert("SwipeView", "Favourite", "Ok"); });
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "delete", sender => { DisplayAlert("SwipeView", "Delete", "Ok"); });
+ }
+ }
+
+ [Preserve(AllMembers = true)]
+ public class Message
+ {
+ public string Title { get; set; }
+ public string SubTitle { get; set; }
+ public string Description { get; set; }
+ public string Date { get; set; }
+ }
+
+ [Preserve(AllMembers = true)]
+ public class SwipeViewGalleryViewModel : BindableObject
+ {
+ private ObservableCollection<Message> _messages;
+
+ public SwipeViewGalleryViewModel()
+ {
+ Messages = new ObservableCollection<Message>();
+ LoadMessages();
+ }
+
+ public ObservableCollection<Message> Messages
+ {
+ get { return _messages; }
+ set
+ {
+ _messages = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public ICommand FavouriteCommand => new Command(OnFavourite);
+ public ICommand DeleteCommand => new Command(OnDelete);
+
+
+ private void LoadMessages()
+ {
+ for (int i = 0; i < 100; i++)
+ {
+ Messages.Add(new Message { Title = $"Lorem ipsum {i + 1}", SubTitle = "Lorem ipsum dolor sit amet", Date = "Yesterday", Description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." });
+ }
+ }
+
+ private void OnFavourite()
+ {
+ MessagingCenter.Send(this, "favourite");
+ }
+
+ private void OnDelete()
+ {
+ MessagingCenter.Send(this, "delete");
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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="Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries.SwipeCarouselViewGallery"
+ Title="Swipe CarouselView">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+
+ <Style x:Key="TitleStyle" TargetType="Label">
+ <Setter Property="FontSize" Value="14" />
+ <Setter Property="TextColor" Value="Black" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="DateStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="SubTitleStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="12" />
+ <Setter Property="Margin" Value="6, 0" />
+ </Style>
+
+ <DataTemplate x:Key="MessageTemplate">
+ <SwipeView
+ HeightRequest="120">
+ <SwipeView.TopItems>
+ <SwipeItems>
+ <SwipeItem
+ Text="Favourite"
+ Icon="calculator.png"
+ BackgroundColor="Yellow"
+ Command="{Binding Source={x:Reference SwipeCollectionView}, Path=BindingContext.FavouriteCommand}"/>
+ </SwipeItems>
+ </SwipeView.TopItems>
+ <SwipeView.BottomItems>
+ <SwipeItems
+ Mode="Execute">
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Command="{Binding Source={x:Reference SwipeCollectionView}, Path=BindingContext.DeleteCommand}"/>
+ </SwipeItems>
+ </SwipeView.BottomItems>
+ <SwipeView.Content>
+ <Grid
+ BackgroundColor="White"
+ HeightRequest="120"
+ RowSpacing="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Label
+ Grid.Column="0"
+ Grid.Row="0"
+ Text="{Binding Title}"
+ Style="{StaticResource TitleStyle}"/>
+ <Label
+ Grid.Column="1"
+ Grid.Row="0"
+ Text="{Binding Date}"
+ Style="{StaticResource DateStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="1"
+ Text="{Binding SubTitle}"
+ Style="{StaticResource SubTitleStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="2"
+ Text="{Binding Description}"
+ Style="{StaticResource SubTitleStyle}"/>
+ </Grid>
+ </SwipeView.Content>
+ </SwipeView>
+ </DataTemplate>
+
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <StackLayout>
+ <CarouselView
+ x:Name="SwipeCollectionView"
+ ItemsSource="{Binding Messages}"
+ ItemTemplate="{StaticResource MessageTemplate}"
+ HeightRequest="120">
+ <CarouselView.ItemsLayout>
+ <LinearItemsLayout
+ Orientation="Horizontal"
+ SnapPointsAlignment="Center"
+ SnapPointsType="MandatorySingle"/>
+ </CarouselView.ItemsLayout>
+ </CarouselView>
+ </StackLayout>
+ </ContentPage.Content>
+</ContentPage>
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public partial class SwipeCarouselViewGallery : ContentPage
+ {
+ public SwipeCarouselViewGallery()
+ {
+ InitializeComponent();
+ BindingContext = new SwipeViewGalleryViewModel();
+
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "favourite", sender => { DisplayAlert("SwipeView", "Favourite", "Ok"); });
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "delete", sender => { DisplayAlert("SwipeView", "Delete", "Ok"); });
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class SwipeCollectionViewGallery : ContentPage
+ {
+ public SwipeCollectionViewGallery()
+ {
+ Title = "CollectionView Galleries";
+ Content = new StackLayout
+ {
+ Children =
+ {
+ GalleryBuilder.NavButton("Horizontal CollectionView Gallery", () => new SwipeHorizontalCollectionViewGallery(), Navigation),
+ GalleryBuilder.NavButton("Vertical CollectionView Gallery", () => new SwipeVerticalCollectionViewGallery(), Navigation)
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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="Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries.SwipeHorizontalCollectionViewGallery"
+ Title="Swipe CollectionView">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+
+ <Style x:Key="TitleStyle" TargetType="Label">
+ <Setter Property="FontSize" Value="14" />
+ <Setter Property="TextColor" Value="Black" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="DateStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="SubTitleStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="12" />
+ <Setter Property="Margin" Value="6, 0" />
+ </Style>
+
+ <DataTemplate x:Key="MessageTemplate">
+ <SwipeView
+ HeightRequest="80">
+ <SwipeView.TopItems>
+ <SwipeItems>
+ <SwipeItem
+ Text="Favourite"
+ Icon="calculator.png"
+ BackgroundColor="Yellow"
+ Command="{Binding Source={x:Reference SwipeCollectionView}, Path=BindingContext.FavouriteCommand}"/>
+ </SwipeItems>
+ </SwipeView.TopItems>
+ <SwipeView.BottomItems>
+ <SwipeItems
+ Mode="Execute">
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Command="{Binding Source={x:Reference SwipeCollectionView}, Path=BindingContext.DeleteCommand}"/>
+ </SwipeItems>
+ </SwipeView.BottomItems>
+ <SwipeView.Content>
+ <Grid
+ BackgroundColor="White"
+ HeightRequest="120"
+ WidthRequest="250"
+ RowSpacing="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Label
+ Grid.Column="0"
+ Grid.Row="0"
+ Text="{Binding Title}"
+ Style="{StaticResource TitleStyle}"/>
+ <Label
+ Grid.Column="1"
+ Grid.Row="0"
+ Text="{Binding Date}"
+ Style="{StaticResource DateStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="1"
+ Text="{Binding SubTitle}"
+ Style="{StaticResource SubTitleStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="2"
+ Text="{Binding Description}"
+ Style="{StaticResource SubTitleStyle}"/>
+ </Grid>
+ </SwipeView.Content>
+ </SwipeView>
+ </DataTemplate>
+
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <StackLayout>
+ <CollectionView
+ x:Name="SwipeCollectionView"
+ ItemsSource="{Binding Messages}"
+ ItemTemplate="{StaticResource MessageTemplate}"
+ HeightRequest="120">
+ <CollectionView.ItemsLayout>
+ <LinearItemsLayout
+ Orientation="Horizontal"/>
+ </CollectionView.ItemsLayout>
+ </CollectionView>
+ </StackLayout>
+ </ContentPage.Content>
+</ContentPage>
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public partial class SwipeHorizontalCollectionViewGallery : ContentPage
+ {
+ public SwipeHorizontalCollectionViewGallery()
+ {
+ InitializeComponent();
+ BindingContext = new SwipeViewGalleryViewModel();
+
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "favourite", sender => { DisplayAlert("SwipeView", "Favourite", "Ok"); });
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "delete", sender => { DisplayAlert("SwipeView", "Delete", "Ok"); });
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ public class SwipeItemIconGallery : ContentPage
+ {
+ public SwipeItemIconGallery()
+ {
+ Title = "SwipeItem Icon Gallery";
+
+ var scroll = new ScrollView();
+
+ var swipeLayout = new StackLayout
+ {
+ Margin = new Thickness(12)
+ };
+
+ var fileSwipeItem = new SwipeItem
+ {
+ BackgroundColor = Color.Red,
+ IconImageSource = "calculator.png",
+ Text = "File"
+ };
+
+ fileSwipeItem.Invoked += (sender, e) => { DisplayAlert("SwipeView", "File Invoked", "Ok"); };
+
+ var fileSwipeItems = new SwipeItems { fileSwipeItem };
+
+ fileSwipeItems.Mode = SwipeMode.Reveal;
+
+ var fileSwipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var fileSwipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Right (File)"
+ };
+
+ fileSwipeContent.Children.Add(fileSwipeLabel);
+
+ var fileSwipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ LeftItems = fileSwipeItems,
+ Content = fileSwipeContent
+ };
+
+ swipeLayout.Children.Add(fileSwipeView);
+
+ var urlSwipeItem = new SwipeItem
+ {
+ BackgroundColor = Color.Red,
+ IconImageSource = "https://image.flaticon.com/icons/png/512/61/61848.png",
+ Text = "Url"
+ };
+
+ urlSwipeItem.Invoked += (sender, e) => { DisplayAlert("SwipeView", "Url Invoked", "Ok"); };
+
+ var urlSwipeItems = new SwipeItems { urlSwipeItem };
+
+ urlSwipeItems.Mode = SwipeMode.Reveal;
+
+ var urlSwipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var urlSwipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Right (Url)"
+ };
+
+ urlSwipeContent.Children.Add(urlSwipeLabel);
+
+ var urlSwipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ LeftItems = urlSwipeItems,
+ Content = urlSwipeContent
+ };
+
+ swipeLayout.Children.Add(urlSwipeView);
+
+ var fontFamily = string.Empty;
+
+ switch (Device.RuntimePlatform)
+ {
+ case Device.iOS:
+ fontFamily = "Ionicons";
+ break;
+ case Device.UWP:
+ fontFamily = "Assets/Fonts/ionicons.ttf#ionicons";
+ break;
+ case Device.Android:
+ default:
+ fontFamily = "fonts/ionicons.ttf#";
+ break;
+ }
+
+ var fontSwipeItem = new SwipeItem
+ {
+ BackgroundColor = Color.Red,
+ IconImageSource = new FontImageSource
+ {
+ Glyph = "\uf101",
+ FontFamily = fontFamily,
+ Size = 16
+ },
+ Text = "Url"
+ };
+
+ fontSwipeItem.Invoked += (sender, e) => { DisplayAlert("SwipeView", "Font Invoked", "Ok"); };
+
+ var fontSwipeItems = new SwipeItems { fontSwipeItem };
+
+ fontSwipeItems.Mode = SwipeMode.Reveal;
+
+ var fontSwipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var fontSwipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Right (Font)"
+ };
+
+ fontSwipeContent.Children.Add(fontSwipeLabel);
+
+ var fontSwipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ LeftItems = fontSwipeItems,
+ Content = fontSwipeContent
+ };
+
+ swipeLayout.Children.Add(fontSwipeView);
+
+ scroll.Content = swipeLayout;
+
+ Content = scroll;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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="Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries.SwipeListViewGallery"
+ Title="Swipe ListView">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+
+ <Style x:Key="TitleStyle" TargetType="Label">
+ <Setter Property="FontSize" Value="14" />
+ <Setter Property="TextColor" Value="Black" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="DateStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="SubTitleStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="12" />
+ <Setter Property="Margin" Value="6, 0" />
+ </Style>
+
+ <DataTemplate x:Key="MessageTemplate">
+ <ViewCell>
+ <SwipeView
+ HeightRequest="80">
+ <Grid
+ BackgroundColor="White"
+ RowSpacing="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Label
+ Grid.Column="0"
+ Grid.Row="0"
+ Text="{Binding Title}"
+ Style="{StaticResource TitleStyle}"/>
+ <Label
+ Grid.Column="1"
+ Grid.Row="0"
+ Text="{Binding Date}"
+ Style="{StaticResource DateStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="1"
+ Text="{Binding SubTitle}"
+ Style="{StaticResource SubTitleStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="2"
+ Text="{Binding Description}"
+ Style="{StaticResource SubTitleStyle}"/>
+ </Grid>
+ <SwipeView.LeftItems>
+ <SwipeItems
+ Mode="Reveal"
+ SwipeBehaviorOnInvoked="Close">
+ <SwipeItem
+ Text="Favourite"
+ Icon="calculator.png"
+ BackgroundColor="Yellow"
+ Command="{Binding Source={x:Reference SwipeListView}, Path=BindingContext.FavouriteCommand}"/>
+ </SwipeItems>
+ </SwipeView.LeftItems>
+ <SwipeView.RightItems>
+ <SwipeItems
+ Mode="Execute">
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Command="{Binding Source={x:Reference SwipeListView}, Path=BindingContext.DeleteCommand}"/>
+ </SwipeItems>
+ </SwipeView.RightItems>
+ </SwipeView>
+ </ViewCell>
+ </DataTemplate>
+
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <Grid>
+ <ListView
+ x:Name="SwipeListView"
+ ItemsSource="{Binding Messages}"
+ ItemTemplate="{StaticResource MessageTemplate}"
+ CachingStrategy="RecycleElement"
+ HasUnevenRows="True"
+ SeparatorVisibility="None" />
+ </Grid>
+ </ContentPage.Content>
+</ContentPage>
\ No newline at end of file
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public partial class SwipeListViewGallery : ContentPage
+ {
+ public SwipeListViewGallery()
+ {
+ InitializeComponent();
+ BindingContext = new SwipeViewGalleryViewModel();
+
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "favourite", sender => { DisplayAlert("SwipeView", "Favourite", "Ok"); });
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "delete", sender => { DisplayAlert("SwipeView", "Delete", "Ok"); });
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Linq;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.PlatformConfiguration;
+using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
+using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class SwipeTransitionModeGallery : ContentPage
+ {
+ public SwipeTransitionModeGallery()
+ {
+ Title = "SwipeTransitionMode Gallery";
+
+ var scroll = new ScrollView();
+
+ var swipeLayout = new StackLayout
+ {
+ Margin = new Thickness(12)
+ };
+
+ var instructions = new Label
+ {
+ BackgroundColor = Color.Black,
+ TextColor = Color.White,
+ Text = "This Gallery use a Platform Specific only available for Android and iOS."
+ };
+
+ swipeLayout.Children.Add(instructions);
+
+ var swipeItemSwipeTransitionModeLabel = new Label
+ {
+ FontSize = 10,
+ Text = "SwipeTransitionMode:"
+ };
+
+ swipeLayout.Children.Add(swipeItemSwipeTransitionModeLabel);
+
+ var swipeItemSwipeTransitionModePicker = new Picker();
+
+ var swipeTransitionModes = Enum.GetNames(typeof(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode)).Select(t => t).ToList();
+
+ swipeItemSwipeTransitionModePicker.ItemsSource = swipeTransitionModes;
+ swipeItemSwipeTransitionModePicker.SelectedIndex = 0; // Reveal
+
+ swipeLayout.Children.Add(swipeItemSwipeTransitionModePicker);
+
+ var deleteSwipeItem = new SwipeItem
+ {
+ BackgroundColor = Color.Red,
+ IconImageSource = "calculator.png",
+ Text = "Delete"
+ };
+
+ deleteSwipeItem.Invoked += (sender, e) => { DisplayAlert("SwipeView", "Delete Invoked", "Ok"); };
+
+ var swipeItems = new SwipeItems { deleteSwipeItem };
+
+ swipeItems.Mode = SwipeMode.Reveal;
+
+ var leftSwipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var leftSwipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Right"
+ };
+
+ leftSwipeContent.Children.Add(leftSwipeLabel);
+
+ var leftSwipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ LeftItems = swipeItems,
+ Content = leftSwipeContent
+ };
+
+ swipeLayout.Children.Add(leftSwipeView);
+
+ var rightSwipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var rightSwipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Left"
+ };
+
+ rightSwipeContent.Children.Add(rightSwipeLabel);
+
+ var rightSwipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ RightItems = swipeItems,
+ Content = rightSwipeContent
+ };
+
+ swipeLayout.Children.Add(rightSwipeView);
+
+ var topSwipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var topSwipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Top"
+ };
+
+ topSwipeContent.Children.Add(topSwipeLabel);
+
+ var topSwipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ BottomItems = swipeItems,
+ Content = topSwipeContent
+ };
+
+ swipeLayout.Children.Add(topSwipeView);
+
+ var bottomSwipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var bottomSwipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Bottom"
+ };
+
+ bottomSwipeContent.Children.Add(bottomSwipeLabel);
+
+ var bottomSwipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ TopItems = swipeItems,
+ Content = bottomSwipeContent
+ };
+
+ swipeLayout.Children.Add(bottomSwipeView);
+
+ swipeItemSwipeTransitionModePicker.SelectedIndexChanged += (sender, e) =>
+ {
+ var swipeTransitionMode = swipeItemSwipeTransitionModePicker.SelectedItem;
+
+ switch(swipeTransitionMode)
+ {
+ case "Drag":
+ leftSwipeView.On<Android>().SetSwipeTransitionMode(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode.Drag);
+ leftSwipeView.On<iOS>().SetSwipeTransitionMode(PlatformConfiguration.iOSSpecific.SwipeTransitionMode.Drag);
+
+ rightSwipeView.On<Android>().SetSwipeTransitionMode(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode.Drag);
+ rightSwipeView.On<iOS>().SetSwipeTransitionMode(PlatformConfiguration.iOSSpecific.SwipeTransitionMode.Drag);
+
+ topSwipeView.On<Android>().SetSwipeTransitionMode(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode.Drag);
+ topSwipeView.On<iOS>().SetSwipeTransitionMode(PlatformConfiguration.iOSSpecific.SwipeTransitionMode.Drag);
+
+ bottomSwipeView.On<Android>().SetSwipeTransitionMode(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode.Drag);
+ bottomSwipeView.On<iOS>().SetSwipeTransitionMode(PlatformConfiguration.iOSSpecific.SwipeTransitionMode.Drag);
+ break;
+ case "Reveal":
+ leftSwipeView.On<Android>().SetSwipeTransitionMode(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode.Reveal);
+ leftSwipeView.On<iOS>().SetSwipeTransitionMode(PlatformConfiguration.iOSSpecific.SwipeTransitionMode.Reveal);
+
+ rightSwipeView.On<Android>().SetSwipeTransitionMode(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode.Drag);
+ rightSwipeView.On<iOS>().SetSwipeTransitionMode(PlatformConfiguration.iOSSpecific.SwipeTransitionMode.Drag);
+
+ topSwipeView.On<Android>().SetSwipeTransitionMode(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode.Drag);
+ topSwipeView.On<iOS>().SetSwipeTransitionMode(PlatformConfiguration.iOSSpecific.SwipeTransitionMode.Drag);
+
+ bottomSwipeView.On<Android>().SetSwipeTransitionMode(PlatformConfiguration.AndroidSpecific.SwipeTransitionMode.Drag);
+ bottomSwipeView.On<iOS>().SetSwipeTransitionMode(PlatformConfiguration.iOSSpecific.SwipeTransitionMode.Drag);
+ break;
+ }
+ };
+
+ scroll.Content = swipeLayout;
+
+ Content = scroll;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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="Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries.SwipeVerticalCollectionViewGallery"
+ Title="Swipe CollectionView">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+
+ <Style x:Key="TitleStyle" TargetType="Label">
+ <Setter Property="FontSize" Value="14" />
+ <Setter Property="TextColor" Value="Black" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="DateStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="10" />
+ <Setter Property="Margin" Value="6, 0, 6, 6" />
+ </Style>
+
+ <Style x:Key="SubTitleStyle" TargetType="Label">
+ <Setter Property="TextColor" Value="DarkGray" />
+ <Setter Property="FontSize" Value="12" />
+ <Setter Property="Margin" Value="6, 0" />
+ </Style>
+
+ <DataTemplate x:Key="MessageTemplate">
+ <SwipeView
+ HeightRequest="80">
+ <SwipeView.LeftItems>
+ <SwipeItems>
+ <SwipeItem
+ Text="Favourite"
+ Icon="calculator.png"
+ BackgroundColor="Yellow"
+ Command="{Binding Source={x:Reference SwipeCollectionView}, Path=BindingContext.FavouriteCommand}"/>
+ </SwipeItems>
+ </SwipeView.LeftItems>
+ <SwipeView.RightItems>
+ <SwipeItems
+ Mode="Execute">
+ <SwipeItem
+ Text="Delete"
+ Icon="coffee.png"
+ BackgroundColor="Red"
+ Command="{Binding Source={x:Reference SwipeCollectionView}, Path=BindingContext.DeleteCommand}"/>
+ </SwipeItems>
+ </SwipeView.RightItems>
+ <SwipeView.Content>
+ <Grid
+ BackgroundColor="White"
+ RowSpacing="0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Label
+ Grid.Column="0"
+ Grid.Row="0"
+ Text="{Binding Title}"
+ Style="{StaticResource TitleStyle}"/>
+ <Label
+ Grid.Column="1"
+ Grid.Row="0"
+ Text="{Binding Date}"
+ Style="{StaticResource DateStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="1"
+ Text="{Binding SubTitle}"
+ Style="{StaticResource SubTitleStyle}"/>
+ <Label
+ Grid.Column="0"
+ Grid.ColumnSpan="2"
+ Grid.Row="2"
+ Text="{Binding Description}"
+ Style="{StaticResource SubTitleStyle}"/>
+ </Grid>
+ </SwipeView.Content>
+ </SwipeView>
+ </DataTemplate>
+
+ </ResourceDictionary>
+ </ContentPage.Resources>
+ <ContentPage.Content>
+ <Grid>
+ <CollectionView
+ x:Name="SwipeCollectionView"
+ ItemsSource="{Binding Messages}"
+ ItemTemplate="{StaticResource MessageTemplate}" />
+ </Grid>
+ </ContentPage.Content>
+</ContentPage>
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public partial class SwipeVerticalCollectionViewGallery : ContentPage
+ {
+ public SwipeVerticalCollectionViewGallery()
+ {
+ InitializeComponent();
+ BindingContext = new SwipeViewGalleryViewModel();
+
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "favourite", sender => { DisplayAlert("SwipeView", "Favourite", "Ok"); });
+ MessagingCenter.Subscribe<SwipeViewGalleryViewModel>(this, "delete", sender => { DisplayAlert("SwipeView", "Delete", "Ok"); });
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class SwipeViewEventsGallery : ContentPage
+ {
+ public SwipeViewEventsGallery()
+ {
+ Title = "SwipeView Events Gallery";
+
+ var swipeLayout = new Grid
+ {
+ RowDefinitions = new RowDefinitionCollection
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star }
+ },
+ Margin = new Thickness(12)
+ };
+
+ var deleteSwipeItem = new SwipeItem
+ {
+ BackgroundColor = Color.Orange,
+ IconImageSource = "calculator.png",
+ Text = "SwipeItem1"
+ };
+
+ deleteSwipeItem.Invoked += (sender, e) => { DisplayAlert("SwipeView", "Delete Invoked", "Ok"); };
+
+ var leftSwipeItems = new SwipeItems
+ {
+ deleteSwipeItem
+ };
+
+ leftSwipeItems.Mode = SwipeMode.Execute;
+
+ var swipeContent = new Grid
+ {
+ BackgroundColor = Color.Gray
+ };
+
+ var swipeLabel = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Text = "Swipe to Right"
+ };
+
+ swipeContent.Children.Add(swipeLabel);
+
+ var scroll = new ScrollView();
+ var eventsInfo = new Label();
+ scroll.Content = eventsInfo;
+
+ var swipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ WidthRequest = 300,
+ LeftItems = leftSwipeItems,
+ Content = swipeContent
+ };
+
+ swipeLayout.Children.Add(swipeView, 0, 0);
+ swipeLayout.Children.Add(scroll, 0, 1);
+
+ Content = swipeLayout;
+
+ swipeView.SwipeStarted += (sender, e) =>
+ {
+ eventsInfo.Text += $"SwipeStarted - Direction:{e.SwipeDirection}" + Environment.NewLine;
+ };
+
+ swipeView.SwipeChanging += (sender, e) =>
+ {
+ eventsInfo.Text += $"SwipeChanging - Direction:{e.SwipeDirection}, Offset:{e.Offset}" + Environment.NewLine;
+ };
+
+ swipeView.SwipeEnded += (sender, e) =>
+ {
+ eventsInfo.Text += $"SwipeEnded - Direction:{e.SwipeDirection}" + Environment.NewLine;
+ };
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls.GalleryPages.SwipeViewGalleries
+{
+ [Preserve(AllMembers = true)]
+ public class SwipeViewGallery : ContentPage
+ {
+ public SwipeViewGallery()
+ {
+ var button = new Button
+ {
+ Text = "Enable CarouselView",
+ AutomationId = "EnableCarouselView"
+ };
+ button.Clicked += ButtonClicked;
+
+ Content = new ScrollView
+ {
+ Content = new StackLayout
+ {
+ Children =
+ {
+ button,
+ GalleryBuilder.NavButton("Basic SwipeView Gallery", () => new BasicSwipeGallery(), Navigation),
+ GalleryBuilder.NavButton("SwipeView Events Gallery", () => new SwipeViewEventsGallery(), Navigation),
+ GalleryBuilder.NavButton("SwipeItems from Resource Gallery", () => new ResourceSwipeItemsGallery(), Navigation),
+ GalleryBuilder.NavButton("BindableLayout Gallery", () => new SwipeBindableLayoutGallery(), Navigation),
+ GalleryBuilder.NavButton("ListView (RecycleElement) Gallery", () => new SwipeListViewGallery(), Navigation),
+ GalleryBuilder.NavButton("CollectionView Galleries", () => new SwipeCollectionViewGallery(), Navigation),
+ GalleryBuilder.NavButton("CarouselView Gallery", () => new SwipeCarouselViewGallery(), Navigation),
+ GalleryBuilder.NavButton("SwipeBehaviorOnInvoked Gallery", () => new SwipeBehaviorOnInvokedGallery(), Navigation),
+ GalleryBuilder.NavButton("Custom SwipeItem Galleries", () => new CustomSwipeItemGallery(), Navigation),
+ GalleryBuilder.NavButton("SwipeItem Icon Gallery", () => new SwipeItemIconGallery(), Navigation),
+ GalleryBuilder.NavButton("SwipeTransitionMode Gallery", () => new SwipeTransitionModeGallery(), Navigation),
+ GalleryBuilder.NavButton("Add/Remove SwipeItems Gallery", () => new AddRemoveSwipeItemsGallery(), Navigation),
+ GalleryBuilder.NavButton("Close SwipeView Gallery", () => new CloseSwipeGallery(), Navigation)
+ }
+ }
+ };
+ }
+
+ void ButtonClicked(object sender, System.EventArgs e)
+ {
+ var button = sender as Button;
+
+ button.Text = "CarouselView Enabled!";
+ button.TextColor = Color.Black;
+ button.IsEnabled = false;
+
+ Device.SetFlags(new[] { ExperimentalFlags.CarouselViewExperimental });
+ }
+ }
+}
\ No newline at end of file
</ItemGroup>
<ItemGroup>
<Folder Include="GalleryPages\CollectionViewGalleries\CarouselViewGalleries\" />
+ <Folder Include="GalleryPages\SwipeViewGalleries\" />
</ItemGroup>
<Target Name="CreateControllGalleryConfig" BeforeTargets="Build">
--- /dev/null
+using System;
+using System.Windows.Input;
+
+namespace Xamarin.Forms
+{
+ public interface ISwipeItem
+ {
+ ICommand Command { get; set; }
+ object CommandParameter { get; set; }
+
+ event EventHandler<EventArgs> Invoked;
+ void OnInvoked();
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms
+{
+ public interface ISwipeViewController
+ {
+ void SendSwipeStarted(SwipeStartedEventArgs args);
+ void SendSwipeChanging(SwipeChangingEventArgs args);
+ void SendSwipeEnded(SwipeEndedEventArgs args);
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms.PlatformConfiguration.AndroidSpecific
+{
+ using FormsElement = Forms.SwipeView;
+
+ public enum SwipeTransitionMode
+ {
+ Reveal = 0,
+ Drag = 1
+ }
+
+ public static class SwipeView
+ {
+ public static readonly BindableProperty SwipeTransitionModeProperty = BindableProperty.Create("SwipeTransitionMode", typeof(SwipeTransitionMode), typeof(SwipeView), SwipeTransitionMode.Reveal);
+
+ public static SwipeTransitionMode GetSwipeTransitionMode(BindableObject element)
+ {
+ return (SwipeTransitionMode)element.GetValue(SwipeTransitionModeProperty);
+ }
+
+ public static void SetSwipeTransitionMode(BindableObject element, SwipeTransitionMode value)
+ {
+ element.SetValue(SwipeTransitionModeProperty, value);
+ }
+
+ public static SwipeTransitionMode GetSwipeTransitionMode(this IPlatformElementConfiguration<Android, FormsElement> config)
+ {
+ return GetSwipeTransitionMode(config.Element);
+ }
+
+ public static IPlatformElementConfiguration<Android, FormsElement> SetSwipeTransitionMode(this IPlatformElementConfiguration<Android, FormsElement> config, SwipeTransitionMode value)
+ {
+ SetSwipeTransitionMode(config.Element, value);
+ return config;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms.PlatformConfiguration.iOSSpecific
+{
+ using FormsElement = Forms.SwipeView;
+
+ public enum SwipeTransitionMode
+ {
+ Reveal = 0,
+ Drag = 1
+ }
+
+ public static class SwipeView
+ {
+ public static readonly BindableProperty SwipeTransitionModeProperty = BindableProperty.Create("SwipeTransitionMode", typeof(SwipeTransitionMode), typeof(SwipeView), SwipeTransitionMode.Reveal);
+
+ public static SwipeTransitionMode GetSwipeTransitionMode(BindableObject element)
+ {
+ return (SwipeTransitionMode)element.GetValue(SwipeTransitionModeProperty);
+ }
+
+ public static void SetSwipeTransitionMode(BindableObject element, SwipeTransitionMode value)
+ {
+ element.SetValue(SwipeTransitionModeProperty, value);
+ }
+
+ public static SwipeTransitionMode GetSwipeTransitionMode(this IPlatformElementConfiguration<iOS, FormsElement> config)
+ {
+ return GetSwipeTransitionMode(config.Element);
+ }
+
+ public static IPlatformElementConfiguration<iOS, FormsElement> SetSwipeTransitionMode(this IPlatformElementConfiguration<iOS, FormsElement> config, SwipeTransitionMode value)
+ {
+ SetSwipeTransitionMode(config.Element, value);
+ return config;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms
+{
+ public enum SwipeBehaviorOnInvoked
+ {
+ Auto, // In Reveal mode, the SwipeView closes after an item is invoked. In Execute mode, the SwipeView remains open.
+ Close, // The SwipeView closes after an item is invoked.
+ RemainOpen // The SwipeView remains open after an item is invoked.
+ }
+}
\ No newline at end of file
using System;
+using System.ComponentModel;
+
namespace Xamarin.Forms
{
[Flags]
Up = 4,
Down = 8
}
+}
+
+namespace Xamarin.Forms.Internals
+{
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static class SwipeDirectionHelper
+ {
+ public static SwipeDirection GetSwipeDirection(Point initialPoint, Point endPoint)
+ {
+ var angle = GetAngleFromPoints(initialPoint.X, initialPoint.Y, endPoint.X, endPoint.Y);
+ return GetSwipeDirectionFromAngle(angle);
+ }
+
+ internal static double GetAngleFromPoints(double x1, double y1, double x2, double y2)
+ {
+ double rad = Math.Atan2(y1 - y2, x2 - x1) + Math.PI;
+ return (rad * 180 / Math.PI + 180) % 360;
+ }
+
+ internal static SwipeDirection GetSwipeDirectionFromAngle(double angle)
+ {
+ if (IsAngleInRange(angle, 45, 135))
+ return SwipeDirection.Up;
+
+ if (IsAngleInRange(angle, 0, 45) || IsAngleInRange(angle, 315, 360))
+ return SwipeDirection.Right;
+
+ if (IsAngleInRange(angle, 225, 315))
+ return SwipeDirection.Down;
+
+ return SwipeDirection.Left;
+ }
+
+ internal static bool IsAngleInRange(double angle, float init, float end)
+ {
+ return (angle >= init) && (angle < end);
+ }
+ }
}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Xamarin.Forms
+{
+ public abstract class BaseSwipeEventArgs : EventArgs
+ {
+ protected BaseSwipeEventArgs(SwipeDirection swipeDirection)
+ {
+ SwipeDirection = swipeDirection;
+ }
+
+ public SwipeDirection SwipeDirection { get; set; }
+ }
+
+ public class SwipeStartedEventArgs : BaseSwipeEventArgs
+ {
+ public SwipeStartedEventArgs(SwipeDirection swipeDirection) : base(swipeDirection)
+ {
+
+ }
+ }
+
+ public class SwipeChangingEventArgs : BaseSwipeEventArgs
+ {
+ public SwipeChangingEventArgs(SwipeDirection swipeDirection, double offset) : base(swipeDirection)
+ {
+ Offset = offset;
+ }
+
+ public double Offset { get; set; }
+ }
+
+ public class SwipeEndedEventArgs : BaseSwipeEventArgs
+ {
+ public SwipeEndedEventArgs(SwipeDirection swipeDirection) : base(swipeDirection)
+ {
+
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.ComponentModel;
+
+namespace Xamarin.Forms
+{
+ public class SwipeItem : MenuItem, ISwipeItem
+ {
+ public static readonly BindableProperty BackgroundColorProperty = BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(SwipeItem), Color.Default);
+
+ public Color BackgroundColor
+ {
+ get { return (Color)GetValue(BackgroundColorProperty); }
+ set { SetValue(BackgroundColorProperty, value); }
+ }
+
+ public event EventHandler<EventArgs> Invoked;
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void OnInvoked()
+ {
+ if (Command != null && Command.CanExecute(CommandParameter))
+ Command.Execute(CommandParameter);
+
+ Invoked?.Invoke(this, EventArgs.Empty);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.ComponentModel;
+using System.Windows.Input;
+
+namespace Xamarin.Forms
+{
+ public class SwipeItemView : ContentView, ISwipeItem
+ {
+ public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(SwipeItemView));
+
+ public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(SwipeItemView));
+
+ public ICommand Command
+ {
+ get => (ICommand)GetValue(CommandProperty);
+ set => SetValue(CommandProperty, value);
+ }
+
+ public object CommandParameter
+ {
+ get => GetValue(CommandParameterProperty);
+ set => SetValue(CommandParameterProperty, value);
+ }
+
+ public event EventHandler<EventArgs> Invoked;
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void OnInvoked()
+ {
+ if (Command != null && Command.CanExecute(CommandParameter))
+ Command.Execute(CommandParameter);
+
+ Invoked?.Invoke(this, EventArgs.Empty);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+
+namespace Xamarin.Forms
+{
+ public class SwipeItems : Element, IList<ISwipeItem>, INotifyCollectionChanged
+ {
+ readonly ObservableCollection<ISwipeItem> _internal;
+
+ public SwipeItems()
+ {
+ _internal = new ObservableCollection<ISwipeItem>();
+ _internal.CollectionChanged += OnSwipeItemsChanged;
+ }
+
+ public static readonly BindableProperty ModeProperty = BindableProperty.Create(nameof(Mode), typeof(SwipeMode), typeof(SwipeItems), SwipeMode.Reveal);
+ public static readonly BindableProperty SwipeBehaviorOnInvokedProperty = BindableProperty.Create(nameof(SwipeBehaviorOnInvoked), typeof(SwipeBehaviorOnInvoked), typeof(SwipeItems), SwipeBehaviorOnInvoked.Auto);
+
+ public SwipeMode Mode
+ {
+ get { return (SwipeMode)GetValue(ModeProperty); }
+ set { SetValue(ModeProperty, value); }
+ }
+
+ public SwipeBehaviorOnInvoked SwipeBehaviorOnInvoked
+ {
+ get { return (SwipeBehaviorOnInvoked)GetValue(SwipeBehaviorOnInvokedProperty); }
+ set { SetValue(SwipeBehaviorOnInvokedProperty, value); }
+ }
+
+ public event NotifyCollectionChangedEventHandler CollectionChanged
+ {
+ add { _internal.CollectionChanged += value; }
+ remove { _internal.CollectionChanged -= value; }
+ }
+
+ public ISwipeItem this[int index]
+ {
+ get => _internal.Count > index ? _internal[index] : null;
+ set => _internal[index] = value;
+ }
+
+ public int Count => _internal.Count;
+
+ public bool IsReadOnly => false;
+
+ public void Add(ISwipeItem item)
+ {
+ _internal.Add(item);
+ }
+
+ public void Clear()
+ {
+ _internal.Clear();
+ }
+
+ public bool Contains(ISwipeItem item)
+ {
+ return _internal.Contains(item);
+ }
+
+ public void CopyTo(ISwipeItem[] array, int arrayIndex)
+ {
+ _internal.CopyTo(array, arrayIndex);
+ }
+
+ public IEnumerator<ISwipeItem> GetEnumerator()
+ {
+ return _internal.GetEnumerator();
+ }
+
+ public int IndexOf(ISwipeItem item)
+ {
+ return _internal.IndexOf(item);
+ }
+
+ public void Insert(int index, ISwipeItem item)
+ {
+ _internal.Insert(index, item);
+ }
+
+ public bool Remove(ISwipeItem item)
+ {
+ return _internal.Remove(item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ _internal.RemoveAt(index);
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+
+ object bc = BindingContext;
+
+ foreach (BindableObject item in _internal)
+ SetInheritedBindingContext(item, bc);
+ }
+
+ void OnSwipeItemsChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
+ {
+ if (notifyCollectionChangedEventArgs.NewItems == null)
+ return;
+
+ object bc = BindingContext;
+
+ foreach (BindableObject item in notifyCollectionChangedEventArgs.NewItems)
+ SetInheritedBindingContext(item, bc);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return _internal.GetEnumerator();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms
+{
+ public enum SwipeMode
+ {
+ Reveal, // Display additional context items which may be selected
+ Execute // Immediately execute the associated command
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.ComponentModel;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Content")]
+ [RenderWith(typeof(_SwipeViewRenderer))]
+ public class SwipeView : ContentView, IElementConfiguration<SwipeView>, ISwipeViewController
+ {
+ readonly Lazy<PlatformConfigurationRegistry<SwipeView>> _platformConfigurationRegistry;
+
+ public SwipeView()
+ {
+ _platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<SwipeView>>(() => new PlatformConfigurationRegistry<SwipeView>(this));
+ }
+
+ public static readonly BindableProperty LeftItemsProperty = BindableProperty.Create(nameof(LeftItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
+ public static readonly BindableProperty RightItemsProperty = BindableProperty.Create(nameof(RightItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
+ public static readonly BindableProperty TopItemsProperty = BindableProperty.Create(nameof(TopItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
+ public static readonly BindableProperty BottomItemsProperty = BindableProperty.Create(nameof(BottomItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
+
+ public SwipeItems LeftItems
+ {
+ get { return (SwipeItems)GetValue(LeftItemsProperty); }
+ set { SetValue(LeftItemsProperty, value); }
+ }
+
+ public SwipeItems RightItems
+ {
+ get { return (SwipeItems)GetValue(RightItemsProperty); }
+ set { SetValue(RightItemsProperty, value); }
+ }
+
+ public SwipeItems TopItems
+ {
+ get { return (SwipeItems)GetValue(TopItemsProperty); }
+ set { SetValue(TopItemsProperty, value); }
+ }
+
+ public SwipeItems BottomItems
+ {
+ get { return (SwipeItems)GetValue(BottomItemsProperty); }
+ set { SetValue(BottomItemsProperty, value); }
+ }
+
+ public event EventHandler<SwipeStartedEventArgs> SwipeStarted;
+ public event EventHandler<SwipeChangingEventArgs> SwipeChanging;
+ public event EventHandler<SwipeEndedEventArgs> SwipeEnded;
+ public event EventHandler CloseRequested;
+
+ public void Close()
+ {
+ CloseRequested?.Invoke(this, EventArgs.Empty);
+ }
+
+ void ISwipeViewController.SendSwipeStarted(SwipeStartedEventArgs args) => SwipeStarted?.Invoke(this, args);
+
+ void ISwipeViewController.SendSwipeChanging(SwipeChangingEventArgs args) => SwipeChanging?.Invoke(this, args);
+
+ void ISwipeViewController.SendSwipeEnded(SwipeEndedEventArgs args) => SwipeEnded?.Invoke(this, args);
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+
+ object bc = BindingContext;
+
+ if (LeftItems != null)
+ SetInheritedBindingContext(LeftItems, bc);
+
+ if (RightItems != null)
+ SetInheritedBindingContext(RightItems, bc);
+
+ if (TopItems != null)
+ SetInheritedBindingContext(TopItems, bc);
+
+ if (BottomItems != null)
+ SetInheritedBindingContext(BottomItems, bc);
+ }
+
+ SwipeItems SwipeItemsDefaultValueCreator() => new SwipeItems();
+
+ static object SwipeItemsDefaultValueCreator(BindableObject bindable)
+ {
+ return ((SwipeView)bindable).SwipeItemsDefaultValueCreator();
+ }
+
+ public IPlatformElementConfiguration<T, SwipeView> On<T>() where T : IConfigPlatform
+ {
+ return _platformConfigurationRegistry.Value.On<T>();
+ }
+ }
+}
\ No newline at end of file
EntryCell,
Editor,
DatePicker,
- CheckBox
+ CheckBox,
+ SwipeView
}
public enum Layouts
ThumbColor
}
+ public enum SwipeView
+ {
+ RightItems,
+ TopItems,
+ BottomItems
+ }
+
public enum CheckBox
{
IsChecked,
[assembly: ExportRenderer (typeof (WebView), typeof (WebViewRenderer))]
[assembly: ExportRenderer (typeof (SearchBar), typeof (SearchBarRenderer))]
[assembly: ExportRenderer (typeof (Switch), typeof (SwitchRenderer))]
+[assembly: ExportRenderer(typeof(SwipeView), typeof(SwipeViewRenderer))]
[assembly: ExportRenderer (typeof (DatePicker), typeof (DatePickerRenderer))]
[assembly: ExportRenderer (typeof (TimePicker), typeof (TimePickerRenderer))]
[assembly: ExportRenderer (typeof (Picker), typeof (PickerRenderer))]
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using Android.Content;
+using Android.Graphics;
+using Android.Graphics.Drawables;
+using Android.Support.V4.Widget;
+using Android.Support.V7.Widget;
+using Android.Views;
+using Android.Widget;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
+using AButton = Android.Support.V7.Widget.AppCompatButton;
+using APointF = Android.Graphics.PointF;
+using ATextAlignment = Android.Views.TextAlignment;
+using AView = Android.Views.View;
+using Specifics = Xamarin.Forms.PlatformConfiguration.AndroidSpecific.SwipeView;
+
+namespace Xamarin.Forms.Platform.Android
+{
+ public class SwipeViewRenderer : ViewRenderer<SwipeView, AView>, GestureDetector.IOnGestureListener
+ {
+ const int SwipeThreshold = 250;
+ const int SwipeThresholdMargin = 6;
+ const int SwipeItemWidth = 100;
+ const long SwipeAnimationDuration = 200;
+
+ readonly Context _context;
+ GestureDetector _detector;
+ AView _scrollParent;
+ AView _contentView;
+ LinearLayoutCompat _actionView;
+ SwipeTransitionMode _swipeTransitionMode;
+ float _downX;
+ float _downY;
+ float _density;
+ bool _isTouchDown;
+ bool _isSwiping;
+ APointF _initialPoint;
+ SwipeDirection? _swipeDirection;
+ float _swipeOffset;
+ float _swipeThreshold;
+ bool _isDisposed;
+
+ public SwipeViewRenderer(Context context) : base(context)
+ {
+ _context = context;
+
+ AutoPackage = false;
+ ClipToOutline = true;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<SwipeView> e)
+ {
+ if (e.NewElement != null)
+ {
+ e.NewElement.CloseRequested += OnCloseRequested;
+
+ if (Control == null)
+ {
+ _density = Resources.DisplayMetrics.Density;
+ _detector = new GestureDetector(Context, this);
+
+ SetNativeControl(CreateNativeControl());
+ }
+
+ UpdateContent();
+ UpdateSwipeTransitionMode();
+ UpdateBackgroundColor();
+ }
+
+ if (e.OldElement != null)
+ e.OldElement.CloseRequested -= OnCloseRequested;
+
+ base.OnElementChanged(e);
+ }
+
+ protected override AView CreateNativeControl()
+ {
+ return new AView(_context);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == ContentView.ContentProperty.PropertyName)
+ UpdateContent();
+ else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ UpdateBackgroundColor();
+ else if (e.PropertyName == Specifics.SwipeTransitionModeProperty.PropertyName)
+ UpdateSwipeTransitionMode();
+ }
+
+ protected override void OnLayout(bool changed, int l, int t, int r, int b)
+ {
+ base.OnLayout(changed, l, t, r, b);
+
+ var width = r - l;
+ var height = b - t;
+
+ var pixelWidth = _context.FromPixels(width);
+ var pixelHeight = _context.FromPixels(height);
+
+ if (changed)
+ {
+ if (Element.Content != null)
+ Element.Content.Layout(new Rectangle(0, 0, pixelWidth, pixelHeight));
+
+ _contentView?.Layout(0, 0, width, height);
+ }
+ }
+
+ protected override Size MinimumSize()
+ {
+ return new Size(40, 40);
+ }
+
+ protected override void UpdateBackgroundColor()
+ {
+ if (Element.BackgroundColor != Color.Default)
+ {
+ var backgroundColor = Element.BackgroundColor.ToAndroid();
+
+ SetBackgroundColor(backgroundColor);
+
+ if (_contentView != null && Element.Content == null && HasSwipeItems())
+ _contentView.SetBackgroundColor(backgroundColor);
+ }
+ }
+
+ protected override void OnAttachedToWindow()
+ {
+ base.OnAttachedToWindow();
+
+ if (Forms.IsLollipopOrNewer && Control != null)
+ {
+ _scrollParent = Parent.GetParentOfType<NestedScrollView>();
+
+ if (_scrollParent != null)
+ {
+ _scrollParent.ScrollChange += OnParentScrollChange;
+ return;
+ }
+
+ _scrollParent = Parent.GetParentOfType<AbsListView>();
+
+ if (_scrollParent is AbsListView listView)
+ {
+ listView.ScrollStateChanged += OnParentScrollStateChanged;
+ return;
+ }
+
+ _scrollParent = Parent.GetParentOfType<RecyclerView>();
+
+ if (_scrollParent != null)
+ {
+ _scrollParent.ScrollChange += OnParentScrollChange;
+ }
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ return;
+
+ if (disposing)
+ {
+ if (Element != null)
+ {
+ Element.CloseRequested -= OnCloseRequested;
+ }
+
+ if (_detector != null)
+ {
+ _detector.Dispose();
+ _detector = null;
+ }
+
+ if (_scrollParent != null)
+ {
+ if (_scrollParent is AbsListView listView)
+ listView.ScrollStateChanged += OnParentScrollStateChanged;
+ else
+ _scrollParent.ScrollChange -= OnParentScrollChange;
+
+ _scrollParent = null;
+ }
+
+ if (_contentView != null)
+ {
+ _contentView.RemoveFromParent();
+ _contentView.Dispose();
+ _contentView = null;
+ }
+
+ if (_actionView != null)
+ {
+ _actionView.RemoveFromParent();
+ _actionView.Dispose();
+ _actionView = null;
+ }
+ }
+
+ _isDisposed = true;
+
+ base.Dispose(disposing);
+ }
+
+ public override bool OnTouchEvent(MotionEvent e)
+ {
+ base.OnTouchEvent(e);
+
+ var density = Resources.DisplayMetrics.Density;
+ float x = Math.Abs((_downX - e.GetX()) / density);
+ float y = Math.Abs((_downY - e.GetY()) / density);
+
+ if (e.Action != MotionEventActions.Move | (x > 10f || y > 10f))
+ {
+ _detector.OnTouchEvent(e);
+ }
+
+ ProcessSwipingInteractions(e);
+
+ return true;
+ }
+
+ public override bool OnInterceptTouchEvent(MotionEvent ev)
+ {
+ return true;
+ }
+
+ public bool OnDown(MotionEvent e)
+ {
+ return true;
+ }
+
+ public bool OnFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
+ {
+ return true;
+ }
+
+ public void OnLongPress(MotionEvent e)
+ {
+
+ }
+
+ public bool OnScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
+ {
+ return true;
+ }
+
+ public void OnShowPress(MotionEvent e)
+ {
+
+ }
+
+ public bool OnSingleTapUp(MotionEvent e)
+ {
+ return true;
+ }
+
+ void UpdateContent()
+ {
+ if (Element.Content == null)
+ _contentView = CreateEmptyContent();
+ else
+ _contentView = CreateContent();
+
+ AddView(_contentView, new LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent));
+ }
+
+ AView CreateEmptyContent()
+ {
+ var emptyContentView = new AView(_context);
+ emptyContentView.SetBackgroundColor(Color.Default.ToAndroid());
+
+ return emptyContentView;
+ }
+
+ AView CreateContent()
+ {
+ var renderer = Platform.CreateRenderer(Element.Content, _context);
+ Platform.SetRenderer(Element.Content, renderer);
+
+ return renderer?.View;
+ }
+
+ SwipeItems GetSwipeItemsByDirection()
+ {
+ SwipeItems swipeItems = null;
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ swipeItems = Element.RightItems;
+ break;
+ case SwipeDirection.Right:
+ swipeItems = Element.LeftItems;
+ break;
+ case SwipeDirection.Up:
+ swipeItems = Element.BottomItems;
+ break;
+ case SwipeDirection.Down:
+ swipeItems = Element.TopItems;
+ break;
+ }
+
+ return swipeItems;
+ }
+
+ bool HasSwipeItems()
+ {
+ return Element != null && (IsValidSwipeItems(Element.LeftItems) || IsValidSwipeItems(Element.RightItems) || IsValidSwipeItems(Element.TopItems) || IsValidSwipeItems(Element.BottomItems));
+ }
+
+ bool IsHorizontalSwipe()
+ {
+ return _swipeDirection == SwipeDirection.Left || _swipeDirection == SwipeDirection.Right;
+ }
+
+ bool IsValidSwipeItems(SwipeItems swipeItems)
+ {
+ return swipeItems != null && swipeItems.Count > 0;
+ }
+
+ bool ProcessSwipingInteractions(MotionEvent e)
+ {
+ bool? handled = true;
+ var point = new APointF(e.GetX() / _density, e.GetY() / _density);
+
+ switch (e.Action)
+ {
+ case MotionEventActions.Down:
+ _downX = e.RawX;
+ _downY = e.RawY;
+
+ handled = HandleTouchInteractions(GestureStatus.Started, point);
+
+ if (handled == true)
+ Parent.RequestDisallowInterceptTouchEvent(true);
+
+ break;
+ case MotionEventActions.Up:
+ handled = HandleTouchInteractions(GestureStatus.Completed, point);
+
+ if (Parent == null)
+ break;
+
+ Parent.RequestDisallowInterceptTouchEvent(false);
+ break;
+ case MotionEventActions.Move:
+ handled = HandleTouchInteractions(GestureStatus.Running, point);
+
+ if (handled == true || Parent == null)
+ break;
+
+ Parent.RequestDisallowInterceptTouchEvent(true);
+ break;
+ case MotionEventActions.Cancel:
+ handled = HandleTouchInteractions(GestureStatus.Canceled, point);
+
+ if (Parent == null)
+ break;
+
+ Parent.RequestDisallowInterceptTouchEvent(false);
+ break;
+ }
+
+ if (handled.HasValue)
+ return !handled.Value;
+
+ return false;
+ }
+
+ bool HandleTouchInteractions(GestureStatus status, APointF point)
+ {
+ switch (status)
+ {
+ case GestureStatus.Started:
+ return !ProcessTouchDown(point);
+ case GestureStatus.Running:
+ return !ProcessTouchMove(point);
+ case GestureStatus.Completed:
+ ProcessTouchUp(point);
+ break;
+ }
+
+ _isTouchDown = false;
+
+ return true;
+ }
+
+ bool ProcessTouchDown(APointF point)
+ {
+ if (_isSwiping || _isTouchDown || _contentView == null)
+ return false;
+
+ if (TouchInsideContent(point))
+ ResetSwipe();
+
+ _initialPoint = point;
+ _isTouchDown = true;
+
+ return true;
+ }
+
+ bool ProcessTouchMove(APointF point)
+ {
+ if (_contentView == null)
+ return false;
+
+ if (!_isSwiping)
+ {
+ _swipeDirection = SwipeDirectionHelper.GetSwipeDirection(new Point(_initialPoint.X, _initialPoint.Y), new Point(point.X, point.Y));
+ RaiseSwipeStarted();
+ _isSwiping = true;
+ }
+
+ if (!ValidateSwipeDirection())
+ return false;
+
+ _swipeOffset = GetSwipeOffset(_initialPoint, point);
+ UpdateSwipeItems();
+
+ if (Math.Abs(_swipeOffset) > double.Epsilon)
+ Swipe();
+ else
+ ResetSwipe();
+
+ RaiseSwipeChanging();
+
+ return true;
+ }
+
+ bool ProcessTouchUp(APointF point)
+ {
+ _isTouchDown = false;
+
+ if(!TouchInsideContent(point))
+ ProcessTouchSwipeItems(point);
+
+ if (!_isSwiping)
+ return false;
+
+ _isSwiping = false;
+
+ RaiseSwipeEnded();
+
+ if (!ValidateSwipeDirection())
+ return false;
+
+ ValidateSwipeThreshold();
+
+ return false;
+ }
+
+ bool TouchInsideContent(APointF point)
+ {
+ if (_contentView == null)
+ return false;
+
+ bool touchContent = TouchInsideContent(_contentView.Left + _contentView.TranslationX, _contentView.Top + _contentView.TranslationY, _contentView.Width, _contentView.Height, _context.ToPixels(point.X), _context.ToPixels(point.Y));
+
+ return touchContent;
+ }
+
+ bool TouchInsideContent(double x1, double y1, double x2, double y2, double x, double y)
+ {
+ if (x > x1 && x < (x1 + x2) && y > y1 && y < (y1 + y2))
+ return true;
+
+ return false;
+ }
+
+ bool ValidateSwipeDirection()
+ {
+ var swipeItems = GetSwipeItemsByDirection();
+ return IsValidSwipeItems(swipeItems);
+ }
+
+ float GetSwipeOffset(APointF initialPoint, APointF endPoint)
+ {
+ float swipeOffset = 0;
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ case SwipeDirection.Right:
+ swipeOffset = endPoint.X - initialPoint.X;
+ break;
+ case SwipeDirection.Up:
+ case SwipeDirection.Down:
+ swipeOffset = endPoint.Y - initialPoint.Y;
+ break;
+ }
+
+ return swipeOffset;
+ }
+
+ void UpdateSwipeItems()
+ {
+ if (_contentView == null || _actionView != null)
+ return;
+
+ var items = GetSwipeItemsByDirection();
+
+ if (items == null)
+ return;
+
+ _actionView = new LinearLayoutCompat(_context);
+
+ using (var layoutParams = new LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent))
+ _actionView.LayoutParameters = layoutParams;
+
+ _actionView.Orientation = LinearLayoutCompat.Horizontal;
+
+ var swipeItems = new List<AView>();
+
+ foreach (var item in items)
+ {
+ AView swipeItem = null;
+
+ if (item is SwipeItem formsSwipeItem)
+ {
+ swipeItem = CreateSwipeItem(formsSwipeItem);
+ _actionView.AddView(swipeItem);
+ }
+
+ if (item is SwipeItemView formsSwipeItemView)
+ {
+ swipeItem = CreateSwipeItemView(formsSwipeItemView);
+ _actionView.AddView(swipeItem);
+ UpdateSwipeItemViewLayout(formsSwipeItemView);
+ }
+
+ swipeItems.Add(swipeItem);
+ }
+
+ AddView(_actionView);
+ _contentView?.BringToFront();
+
+ _actionView.Layout(0, 0, _contentView.Width, _contentView.Height);
+ LayoutSwipeItems(swipeItems);
+ swipeItems.Clear();
+ }
+
+ void LayoutSwipeItems(List<AView> childs)
+ {
+ if (_actionView == null || childs == null)
+ return;
+
+ var items = GetSwipeItemsByDirection();
+ int i = 0;
+ int previousWidth = 0;
+
+ foreach (var child in childs)
+ {
+ var item = items[i];
+ var swipeItemSize = GetSwipeItemSize(item);
+ var swipeItemHeight = (int)_context.ToPixels(swipeItemSize.Height);
+ var swipeItemWidth = (int)_context.ToPixels(swipeItemSize.Width);
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ child.Layout(_contentView.Width - (swipeItemWidth + previousWidth), 0, _contentView.Width - previousWidth, swipeItemHeight);
+ break;
+ case SwipeDirection.Right:
+ case SwipeDirection.Up:
+ case SwipeDirection.Down:
+ child.Layout(previousWidth, 0, (i + 1) * swipeItemWidth, swipeItemHeight);
+ break;
+ }
+
+ i++;
+ previousWidth += swipeItemWidth;
+ }
+ }
+
+ AView CreateSwipeItem(SwipeItem formsSwipeItem)
+ {
+ var swipeButton = new AButton(_context)
+ {
+ Background = new ColorDrawable(formsSwipeItem.BackgroundColor.ToAndroid()),
+ Text = formsSwipeItem.Text
+ };
+
+ var textColor = GetSwipeItemColor(formsSwipeItem.BackgroundColor);
+ swipeButton.SetTextColor(textColor.ToAndroid());
+ swipeButton.TextAlignment = ATextAlignment.Center;
+
+ int contentHeight = _contentView.Height;
+ int contentWidth = (int)_context.ToPixels(SwipeItemWidth);
+ int iconSize = Math.Min(contentHeight, contentWidth) / 2;
+
+ _ = this.ApplyDrawableAsync(formsSwipeItem, MenuItem.IconImageSourceProperty, Context, drawable =>
+ {
+ drawable.SetBounds(0, 0, iconSize, iconSize);
+ drawable.SetColorFilter(textColor.ToAndroid(), PorterDuff.Mode.SrcAtop);
+ swipeButton.SetCompoundDrawables(null, drawable, null, null);
+ });
+
+ var textSize = (int)swipeButton.TextSize;
+ var buttonPadding = (contentHeight - (iconSize + textSize + 6)) / 2;
+ swipeButton.SetPadding(0, buttonPadding, 0, buttonPadding);
+ swipeButton.SetOnTouchListener(null);
+
+ return swipeButton;
+ }
+
+ AView CreateSwipeItemView(SwipeItemView swipeItemView)
+ {
+ var renderer = Platform.CreateRenderer(swipeItemView, _context);
+ Platform.SetRenderer(swipeItemView, renderer);
+ var swipeItem = renderer?.View;
+
+ return swipeItem;
+ }
+
+ void UpdateSwipeItemViewLayout(SwipeItemView swipeItemView)
+ {
+ var swipeItemSize = GetSwipeItemSize(swipeItemView);
+ var swipeItemHeight = swipeItemSize.Height;
+ var swipeItemWidth = swipeItemSize.Width;
+
+ swipeItemView.Layout(new Rectangle(0, 0, swipeItemWidth, swipeItemHeight));
+ swipeItemView.Content?.Layout(new Rectangle(0, 0, swipeItemWidth, swipeItemHeight));
+ }
+
+ void UpdateSwipeTransitionMode()
+ {
+ if (Element.IsSet(Specifics.SwipeTransitionModeProperty))
+ _swipeTransitionMode = Element.OnThisPlatform().GetSwipeTransitionMode();
+ else
+ _swipeTransitionMode = SwipeTransitionMode.Reveal;
+ }
+
+ Color GetSwipeItemColor(Color backgroundColor)
+ {
+ var luminosity = 0.2126 * backgroundColor.R + 0.7152 * backgroundColor.G + 0.0722 * backgroundColor.B;
+
+ return luminosity < 0.75 ? Color.White : Color.Black;
+ }
+
+ void DisposeSwipeItems()
+ {
+ if (_actionView != null)
+ {
+ RemoveView(_actionView);
+ _actionView.Dispose();
+ _actionView = null;
+ }
+ }
+
+ void Swipe()
+ {
+ var offset = _context.ToPixels(ValidateSwipeOffset(_swipeOffset));
+
+ if (_swipeTransitionMode == SwipeTransitionMode.Reveal)
+ {
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ case SwipeDirection.Right:
+ _contentView.TranslationX = offset;
+ break;
+ case SwipeDirection.Up:
+ case SwipeDirection.Down:
+ _contentView.TranslationY = offset;
+ break;
+ }
+ }
+
+ if (_swipeTransitionMode == SwipeTransitionMode.Drag)
+ {
+ int actionSize;
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ _contentView.TranslationX = offset;
+ actionSize = (int)_context.ToPixels(Element.RightItems.Count * SwipeItemWidth);
+ _actionView.TranslationX = actionSize - Math.Abs(offset);
+ break;
+ case SwipeDirection.Right:
+ _contentView.TranslationX = offset;
+ actionSize = (int)_context.ToPixels(Element.LeftItems.Count * SwipeItemWidth);
+ _actionView.TranslationX = -actionSize + offset;
+ break;
+ case SwipeDirection.Up:
+ _contentView.TranslationY = offset;
+ actionSize = _contentView.Height;
+ _actionView.TranslationY = actionSize - Math.Abs(offset);
+ break;
+ case SwipeDirection.Down:
+ _contentView.TranslationY = offset;
+ actionSize = _contentView.Height;
+ _actionView.TranslationY = -actionSize + Math.Abs(offset);
+ break;
+ }
+ }
+ }
+
+ void ResetSwipe()
+ {
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ case SwipeDirection.Right:
+ if (!IsValidSwipeItems(Element.LeftItems) && !IsValidSwipeItems(Element.RightItems))
+ return;
+
+ _swipeDirection = null;
+ _isSwiping = false;
+ _swipeThreshold = 0;
+
+ _contentView.Animate().TranslationX(0).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(DisposeSwipeItems));
+ break;
+ case SwipeDirection.Up:
+ case SwipeDirection.Down:
+ if (!IsValidSwipeItems(Element.TopItems) && !IsValidSwipeItems(Element.BottomItems))
+ return;
+
+ _swipeDirection = null;
+ _isSwiping = false;
+ _swipeThreshold = 0;
+
+ _contentView.Animate().TranslationY(0).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(DisposeSwipeItems));
+ break;
+ }
+ }
+
+ void CompleteSwipe()
+ {
+ float swipeThreshold;
+
+ var swipeItems = GetSwipeItemsByDirection();
+ swipeThreshold = _context.ToPixels(GetSwipeThreshold(swipeItems));
+
+ if (_swipeTransitionMode == SwipeTransitionMode.Reveal)
+ {
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ _contentView.Animate().TranslationX(-swipeThreshold).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(() => { _isSwiping = false; }));
+ break;
+ case SwipeDirection.Right:
+ _contentView.Animate().TranslationX(swipeThreshold).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(() => { _isSwiping = false; }));
+ break;
+ case SwipeDirection.Up:
+ _contentView.Animate().TranslationY(-swipeThreshold).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(() => { _isSwiping = false; }));
+ break;
+ case SwipeDirection.Down:
+ _contentView.Animate().TranslationY(swipeThreshold).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(() => { _isSwiping = false; }));
+ break;
+ }
+ }
+
+ if (_swipeTransitionMode == SwipeTransitionMode.Drag)
+ {
+ int actionSize;
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ _contentView.Animate().TranslationX(-swipeThreshold).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(() => { _isSwiping = false; }));
+ actionSize = (int)_context.ToPixels(Element.RightItems.Count * SwipeItemWidth);
+ _actionView.Animate().TranslationX(actionSize - swipeThreshold).SetDuration(SwipeAnimationDuration);
+ break;
+ case SwipeDirection.Right:
+ _contentView.Animate().TranslationX(swipeThreshold).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(() => { _isSwiping = false; }));
+ actionSize = (int)_context.ToPixels(Element.LeftItems.Count * SwipeItemWidth);
+ _actionView.Animate().TranslationX(-actionSize + swipeThreshold).SetDuration(SwipeAnimationDuration);
+ break;
+ case SwipeDirection.Up:
+ _contentView.Animate().TranslationY(-swipeThreshold).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(() => { _isSwiping = false; }));
+ actionSize = _contentView.Height;
+ _actionView.Animate().TranslationY(actionSize - Math.Abs(swipeThreshold)).SetDuration(SwipeAnimationDuration);
+ break;
+ case SwipeDirection.Down:
+ _contentView.Animate().TranslationY(swipeThreshold).SetDuration(SwipeAnimationDuration).WithEndAction(new Java.Lang.Runnable(() => { _isSwiping = false; }));
+ actionSize = _contentView.Height;
+ _actionView.Animate().TranslationY(-actionSize + Math.Abs(swipeThreshold)).SetDuration(SwipeAnimationDuration);
+ break;
+ }
+ }
+ }
+
+ float ValidateSwipeOffset(float offset)
+ {
+ var swipeThreshold = GetSwipeThreshold();
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ if (offset > 0)
+ offset = 0;
+
+ if (Math.Abs(offset) > swipeThreshold)
+ return -swipeThreshold;
+ break;
+ case SwipeDirection.Right:
+ if (offset < 0)
+ offset = 0;
+
+ if (Math.Abs(offset) > swipeThreshold)
+ return swipeThreshold;
+ break;
+ case SwipeDirection.Up:
+ if (offset > 0)
+ offset = 0;
+
+ if (Math.Abs(offset) > swipeThreshold)
+ return -swipeThreshold;
+ break;
+ case SwipeDirection.Down:
+ if (offset < 0)
+ offset = 0;
+
+ if (Math.Abs(offset) > swipeThreshold)
+ return swipeThreshold;
+ break;
+ }
+
+ return offset;
+ }
+
+ void ValidateSwipeThreshold()
+ {
+ if (_swipeDirection == null)
+ return;
+
+ var swipeThresholdPercent = 0.6 * GetSwipeThreshold();
+
+ if (Math.Abs(_swipeOffset) >= swipeThresholdPercent)
+ {
+ var swipeItems = GetSwipeItemsByDirection();
+
+ if (swipeItems == null)
+ return;
+
+ if (swipeItems.Mode == SwipeMode.Execute)
+ {
+ foreach (var swipeItem in swipeItems)
+ {
+ ExecuteSwipeItem(swipeItem);
+ }
+
+ if (swipeItems.SwipeBehaviorOnInvoked != SwipeBehaviorOnInvoked.RemainOpen)
+ ResetSwipe();
+ }
+ else
+ CompleteSwipe();
+ }
+ else
+ {
+ ResetSwipe();
+ }
+ }
+
+ float GetSwipeThreshold()
+ {
+ if (Math.Abs(_swipeThreshold) > double.Epsilon)
+ return _swipeThreshold;
+
+ var swipeItems = GetSwipeItemsByDirection();
+
+ if (swipeItems == null)
+ return 0;
+
+ _swipeThreshold = GetSwipeThreshold(swipeItems);
+
+ return _swipeThreshold;
+ }
+
+ float GetSwipeThreshold(SwipeItems swipeItems)
+ {
+ float swipeThreshold = 0;
+
+ bool isHorizontal = IsHorizontalSwipe();
+
+ if (swipeItems.Mode == SwipeMode.Reveal)
+ {
+ if (isHorizontal)
+ {
+ foreach (var swipeItem in swipeItems)
+ {
+ var swipeItemSize = GetSwipeItemSize(swipeItem);
+ swipeThreshold += (float)swipeItemSize.Width;
+ }
+ }
+ else
+ swipeThreshold = GetSwipeItemHeight();
+ }
+ else
+ {
+ if (isHorizontal)
+ swipeThreshold = SwipeThreshold;
+ else
+ {
+ var contentHeight = (float)_context.FromPixels(_contentView.Height);
+ swipeThreshold = (SwipeThreshold > contentHeight) ? contentHeight : SwipeThreshold;
+ }
+ }
+
+ return ValidateSwipeThreshold(swipeThreshold);
+ }
+
+ float ValidateSwipeThreshold(float swipeThreshold)
+ {
+ var contentHeight = (float)_context.FromPixels(_contentView.Height);
+ var contentWidth = (float)_context.FromPixels(_contentView.Width);
+ bool isHorizontal = IsHorizontalSwipe();
+
+ if (isHorizontal)
+ {
+ if (swipeThreshold > contentWidth)
+ swipeThreshold = contentWidth;
+
+ return swipeThreshold - SwipeThresholdMargin;
+ }
+
+ if (swipeThreshold > contentHeight)
+ swipeThreshold = contentHeight;
+
+ return swipeThreshold - SwipeThresholdMargin / 2;
+ }
+
+ Size GetSwipeItemSize(ISwipeItem swipeItem)
+ {
+ bool isHorizontal = IsHorizontalSwipe();
+ var items = GetSwipeItemsByDirection();
+ var contentHeight = _context.FromPixels(_contentView.Height);
+ var contentWidth = _context.FromPixels(_contentView.Width);
+
+ if (isHorizontal)
+ {
+ if (swipeItem is SwipeItem)
+ return new Size(items.Mode == SwipeMode.Execute ? contentWidth / items.Count : SwipeItemWidth, contentHeight);
+
+ if (swipeItem is SwipeItemView horizontalSwipeItemView)
+ {
+ var swipeItemViewSizeRequest = horizontalSwipeItemView.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+ return new Size(swipeItemViewSizeRequest.Request.Width > 0 ? (float)swipeItemViewSizeRequest.Request.Width : SwipeItemWidth, contentHeight);
+ }
+ }
+ else
+ {
+ if (swipeItem is SwipeItem)
+ return new Size(contentWidth / items.Count, GetSwipeItemHeight());
+
+ if (swipeItem is SwipeItemView horizontalSwipeItemView)
+ {
+ var swipeItemViewSizeRequest = horizontalSwipeItemView.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+ return new Size(contentWidth / items.Count, swipeItemViewSizeRequest.Request.Height > 0 ? (float)swipeItemViewSizeRequest.Request.Height : contentHeight);
+ }
+ }
+
+ return Size.Zero;
+ }
+
+ float GetSwipeItemHeight()
+ {
+ var contentHeight = (float)_context.FromPixels(_contentView.Height);
+ var items = GetSwipeItemsByDirection();
+
+ if (items.Any(s => s is SwipeItemView))
+ {
+ var itemsHeight = new List<float>();
+
+ foreach (var swipeItem in items)
+ {
+ if (swipeItem is SwipeItemView swipeItemView)
+ {
+ var swipeItemViewSizeRequest = swipeItemView.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+ itemsHeight.Add((float)swipeItemViewSizeRequest.Request.Height);
+ }
+ }
+
+ return itemsHeight.Max();
+ }
+
+ return contentHeight;
+ }
+
+ void ProcessTouchSwipeItems(APointF point)
+ {
+ var swipeItems = GetSwipeItemsByDirection();
+
+ if (swipeItems == null || _actionView == null)
+ return;
+
+ for (int i = 0; i < _actionView.ChildCount; i++)
+ {
+ var swipeButton = _actionView.GetChildAt(i);
+
+ var swipeItemX = swipeButton.Left / _density;
+ var swipeItemY = swipeButton.Top / _density;
+ var swipeItemHeight = swipeButton.Height / _density;
+ var swipeItemWidth = swipeButton.Width / _density;
+
+
+ if (TouchInsideContent(swipeItemX, swipeItemY, swipeItemWidth, swipeItemHeight, point.X, point.Y))
+ {
+ var swipeItem = swipeItems[i];
+
+ ExecuteSwipeItem(swipeItem);
+
+ if (swipeItems.SwipeBehaviorOnInvoked != SwipeBehaviorOnInvoked.RemainOpen)
+ ResetSwipe();
+
+ break;
+ }
+ }
+ }
+
+ void ExecuteSwipeItem(ISwipeItem swipeItem)
+ {
+ if (swipeItem == null)
+ return;
+
+ swipeItem.OnInvoked();
+ }
+
+ void OnCloseRequested(object sender, EventArgs e)
+ {
+ ResetSwipe();
+ }
+
+ void OnParentScrollChange(object sender, ScrollChangeEventArgs e)
+ {
+ if (sender is RecyclerView recyclerView)
+ {
+ var scrollState = (ScrollState)recyclerView.ScrollState;
+
+ if (scrollState == ScrollState.Fling || scrollState == ScrollState.TouchScroll)
+ ResetSwipe();
+ }
+ else
+ {
+ var x = Math.Abs(e.ScrollX - e.OldScrollX);
+ var y = Math.Abs(e.ScrollY - e.OldScrollY);
+
+ if (x > 10 || y > 10)
+ ResetSwipe();
+ }
+ }
+
+ void OnParentScrollStateChanged(object sender, AbsListView.ScrollStateChangedEventArgs e)
+ {
+ if (e.ScrollState == ScrollState.Fling || e.ScrollState == ScrollState.TouchScroll)
+ ResetSwipe();
+ }
+
+ void RaiseSwipeStarted()
+ {
+ if (_swipeDirection == null || !ValidateSwipeDirection())
+ return;
+
+ var swipeStartedEventArgs = new SwipeStartedEventArgs(_swipeDirection.Value);
+ ((ISwipeViewController)Element).SendSwipeStarted(swipeStartedEventArgs);
+ }
+
+ void RaiseSwipeChanging()
+ {
+ if (_swipeDirection == null)
+ return;
+
+ var swipeChangingEventArgs = new SwipeChangingEventArgs(_swipeDirection.Value, _swipeOffset);
+ ((ISwipeViewController)Element).SendSwipeChanging(swipeChangingEventArgs);
+ }
+
+ void RaiseSwipeEnded()
+ {
+ if (_swipeDirection == null || !ValidateSwipeDirection())
+ return;
+
+ var swipeEndedEventArgs = new SwipeEndedEventArgs(_swipeDirection.Value);
+ ((ISwipeViewController)Element).SendSwipeEnded(swipeEndedEventArgs);
+ }
+ }
+}
\ No newline at end of file
<Compile Include="AppCompat\CheckBoxRenderer.cs" />
<Compile Include="AppCompat\CheckBoxDesignerRenderer.cs" />
<Compile Include="Renderers\RefreshViewRenderer.cs" />
+ <Compile Include="Renderers\SwipeViewRenderer.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
throw new InvalidCastException($"\"{source.GetType().FullName}\" is not supported.");
}
+ public static Microsoft.UI.Xaml.Controls.IconSource ToWindowsIconSource(this ImageSource source)
+ {
+ return source.ToWindowsIconSourceAsync().GetAwaiter().GetResult();
+ }
+
+ public static async Task<Microsoft.UI.Xaml.Controls.IconSource> ToWindowsIconSourceAsync(this ImageSource source, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (source == null || source.IsEmpty)
+ return null;
+
+ var handler = Registrar.Registered.GetHandlerForObject<IIconElementHandler>(source);
+ if (handler == null)
+ return null;
+
+ try
+ {
+ return await handler.LoadIconSourceAsync(source, cancellationToken);
+ }
+ catch (OperationCanceledException)
+ {
+ // no-op
+ }
+
+ return null;
+ }
+
public static IconElement ToWindowsIconElement(this ImageSource source)
{
return source.ToWindowsIconElementAsync().GetAwaiter().GetResult();
return Task.FromResult(image);
}
+ public Task<Microsoft.UI.Xaml.Controls.IconSource> LoadIconSourceAsync(ImageSource imagesource, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Microsoft.UI.Xaml.Controls.IconSource image = null;
+
+ if (imagesource is FileImageSource filesource)
+ {
+ string file = filesource.File;
+ image = new Microsoft.UI.Xaml.Controls.BitmapIconSource { UriSource = new Uri("ms-appx:///" + file) };
+ }
+
+ return Task.FromResult(image);
+ }
+
public Task<IconElement> LoadIconElementAsync(ImageSource imagesource, CancellationToken cancellationToken = default(CancellationToken))
{
IconElement image = null;
}
}
+ public Task<Microsoft.UI.Xaml.Controls.IconSource> LoadIconSourceAsync(ImageSource imagesource, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Microsoft.UI.Xaml.Controls.IconSource image = null;
+
+ if (imagesource is FontImageSource fontImageSource)
+ {
+ image = new Microsoft.UI.Xaml.Controls.FontIconSource
+ {
+ Glyph = fontImageSource.Glyph,
+ FontFamily = new FontFamily(fontImageSource.FontFamily),
+ FontSize = fontImageSource.Size,
+ Foreground = fontImageSource.Color.ToBrush()
+ };
+ }
+
+ return Task.FromResult(image);
+ }
+
public Task<IconElement> LoadIconElementAsync(ImageSource imagesource, CancellationToken cancellationToken = default(CancellationToken))
{
IconElement image = null;
{
public interface IIconElementHandler : IRegisterable
{
+ Task<Microsoft.UI.Xaml.Controls.IconSource> LoadIconSourceAsync(ImageSource imagesource, CancellationToken cancellationToken = default(CancellationToken));
Task<IconElement> LoadIconElementAsync(ImageSource imagesource, CancellationToken cancellationToken = default(CancellationToken));
}
-}
+}
\ No newline at end of file
[assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))]
[assembly: ExportRenderer(typeof(Slider), typeof(SliderRenderer))]
[assembly: ExportRenderer(typeof(Switch), typeof(SwitchRenderer))]
+[assembly: ExportRenderer(typeof(SwipeView), typeof(SwipeViewRenderer))]
[assembly: ExportRenderer(typeof(WebView), typeof(WebViewRenderer))]
[assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))]
[assembly: ExportRenderer(typeof(ActivityIndicator), typeof(ActivityIndicatorRenderer))]
--- /dev/null
+using System;
+using System.Linq;
+using System.ComponentModel;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using WSwipeBehaviorOnInvoked = Microsoft.UI.Xaml.Controls.SwipeBehaviorOnInvoked;
+using WSwipeControl = Microsoft.UI.Xaml.Controls.SwipeControl;
+using WSwipeItems = Microsoft.UI.Xaml.Controls.SwipeItems;
+using WSwipeItem = Microsoft.UI.Xaml.Controls.SwipeItem;
+using WSwipeMode = Microsoft.UI.Xaml.Controls.SwipeMode;
+
+namespace Xamarin.Forms.Platform.UWP
+{
+ public class SwipeViewRenderer : ViewRenderer<SwipeView, WSwipeControl>
+ {
+ bool _isDisposed;
+ Dictionary<WSwipeItem, SwipeItem> _leftItems;
+ Dictionary<WSwipeItem, SwipeItem> _rightItems;
+ Dictionary<WSwipeItem, SwipeItem> _topItems;
+ Dictionary<WSwipeItem, SwipeItem> _bottomItems;
+
+ public SwipeViewRenderer()
+ {
+ AutoPackage = false;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<SwipeView> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ e.NewElement.CloseRequested += OnCloseRequested;
+ e.NewElement.LeftItems.CollectionChanged += OnSwipeItemsChanged;
+ e.NewElement.RightItems.CollectionChanged += OnSwipeItemsChanged;
+ e.NewElement.TopItems.CollectionChanged += OnSwipeItemsChanged;
+ e.NewElement.BottomItems.CollectionChanged += OnSwipeItemsChanged;
+
+ if (Control == null)
+ {
+ SetNativeControl(new WSwipeControl());
+ }
+
+ UpdateContent();
+ UpdateSwipeItems();
+ UpdateBackgroundColor();
+ }
+
+ if (e.OldElement != null)
+ {
+ e.OldElement.CloseRequested -= OnCloseRequested;
+ e.OldElement.LeftItems.CollectionChanged -= OnSwipeItemsChanged;
+ e.OldElement.RightItems.CollectionChanged -= OnSwipeItemsChanged;
+ e.OldElement.TopItems.CollectionChanged -= OnSwipeItemsChanged;
+ e.OldElement.BottomItems.CollectionChanged -= OnSwipeItemsChanged;
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs changedProperty)
+ {
+ base.OnElementPropertyChanged(sender, changedProperty);
+
+ if (changedProperty.PropertyName == ContentView.ContentProperty.PropertyName)
+ UpdateContent();
+ else if (changedProperty.IsOneOf(SwipeView.LeftItemsProperty, SwipeView.RightItemsProperty, SwipeView.TopItemsProperty, SwipeView.BottomItemsProperty))
+ UpdateSwipeItems();
+ else if (changedProperty.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ UpdateBackgroundColor();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ if (Element != null)
+ {
+ Element.CloseRequested -= OnCloseRequested;
+
+ if (Element.LeftItems != null)
+ {
+ Element.LeftItems.CollectionChanged -= OnSwipeItemsChanged;
+ Element.LeftItems.PropertyChanged -= OnSwipeItemsPropertyChanged;
+ }
+
+ if (Element.RightItems != null)
+ {
+ Element.RightItems.CollectionChanged -= OnSwipeItemsChanged;
+ Element.RightItems.PropertyChanged -= OnSwipeItemsPropertyChanged;
+ }
+
+ if (Element.TopItems != null)
+ {
+ Element.TopItems.CollectionChanged -= OnSwipeItemsChanged;
+ Element.TopItems.PropertyChanged -= OnSwipeItemsPropertyChanged;
+ }
+
+ if (Element.BottomItems != null)
+ {
+ Element.BottomItems.CollectionChanged -= OnSwipeItemsChanged;
+ Element.BottomItems.PropertyChanged -= OnSwipeItemsPropertyChanged;
+ }
+ }
+
+ if (_leftItems != null)
+ DisposeSwipeItems(_leftItems);
+
+ if (_rightItems != null)
+ DisposeSwipeItems(_rightItems);
+
+ if (_topItems != null)
+ DisposeSwipeItems(_topItems);
+
+ if (_bottomItems != null)
+ DisposeSwipeItems(_bottomItems);
+ }
+
+ _isDisposed = true;
+
+ base.Dispose(disposing);
+ }
+
+ protected override void UpdateBackgroundColor()
+ {
+ Color backgroundColor = Element.BackgroundColor;
+
+ if (Control != null)
+ {
+ Control.Background = backgroundColor.IsDefault ? null : backgroundColor.ToBrush();
+ }
+
+ base.UpdateBackgroundColor();
+ }
+
+ protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
+ {
+ if (Control.Parent != null)
+ return base.MeasureOverride(availableSize);
+ else
+ {
+ if (Element == null || availableSize.Width * availableSize.Height == 0)
+ return new Windows.Foundation.Size(0, 0);
+
+ Element.IsInNativeLayout = true;
+
+ double width = Math.Max(0, Element.Width);
+ double height = Math.Max(0, Element.Height);
+ var result = new Windows.Foundation.Size(width, height);
+
+ if (Control != null)
+ {
+ double w = Element.Width;
+ double h = Element.Height;
+
+ if (w == -1)
+ w = availableSize.Width;
+
+ if (h == -1)
+ h = availableSize.Height;
+
+ w = Math.Max(0, w);
+ h = Math.Max(0, h);
+
+ // SwipeLayout sometimes crashes when Measure if not previously fully loaded into the VisualTree.
+ Control.Loaded += (sender, args) => { Control.Measure(new Windows.Foundation.Size(w, h)); };
+ }
+
+ Element.IsInNativeLayout = false;
+
+ return result;
+ }
+ }
+
+ void OnSwipeItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ UpdateSwipeItems();
+ }
+
+ void OnSwipeItemsPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var formsSwipeItems = sender as SwipeItems;
+
+ if (e.PropertyName == SwipeItems.ModeProperty.PropertyName)
+ UpdateSwipeMode(formsSwipeItems);
+ else if (e.PropertyName == SwipeItems.SwipeBehaviorOnInvokedProperty.PropertyName)
+ UpdateSwipeBehaviorOnInvoked(formsSwipeItems);
+ }
+
+ void OnSwipeItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var formsSwipeItem = sender as SwipeItem;
+
+ if (e.IsOneOf(
+ MenuItem.TextProperty,
+ MenuItem.IconImageSourceProperty,
+ MenuItem.CommandProperty,
+ MenuItem.CommandParameterProperty,
+ VisualElement.BackgroundColorProperty))
+ UpdateSwipeItem(formsSwipeItem);
+ }
+
+ void UpdateContent()
+ {
+ if (Element.Content == null)
+ return;
+
+ var renderer = Element.Content.GetOrCreateRenderer();
+ Control.Content = renderer?.ContainerElement;
+ }
+
+ void UpdateSwipeItems()
+ {
+ if (IsValidSwipeItems(Element.LeftItems))
+ Control.LeftItems = CreateSwipeItems(SwipeDirection.Left);
+
+ if (IsValidSwipeItems(Element.RightItems))
+ Control.RightItems = CreateSwipeItems(SwipeDirection.Right);
+
+ if (IsValidSwipeItems(Element.TopItems))
+ Control.TopItems = CreateSwipeItems(SwipeDirection.Up);
+
+ if (IsValidSwipeItems(Element.BottomItems))
+ Control.BottomItems = CreateSwipeItems(SwipeDirection.Down);
+ }
+
+ void UpdateSwipeMode(SwipeItems swipeItems)
+ {
+ var windowsSwipeItems = GetWindowsSwipeItems(swipeItems);
+
+ if (windowsSwipeItems != null)
+ windowsSwipeItems.Mode = GetSwipeMode(swipeItems.Mode);
+ }
+
+ void UpdateSwipeBehaviorOnInvoked(SwipeItems swipeItems)
+ {
+ var windowsSwipeItems = GetWindowsSwipeItems(swipeItems);
+
+ if (windowsSwipeItems != null)
+ foreach (var windowSwipeItem in windowsSwipeItems.ToList())
+ windowSwipeItem.BehaviorOnInvoked = GetSwipeBehaviorOnInvoked(swipeItems.SwipeBehaviorOnInvoked);
+ }
+
+ void UpdateSwipeItem(SwipeItem formsSwipeItem)
+ {
+ if (formsSwipeItem == null)
+ return;
+
+ var windowsSwipeItem = GetWindowsSwipeItem(formsSwipeItem);
+
+ if (windowsSwipeItem != null)
+ {
+ windowsSwipeItem.Text = formsSwipeItem.Text;
+ windowsSwipeItem.IconSource = formsSwipeItem.IconImageSource.ToWindowsIconSource();
+ windowsSwipeItem.Background = formsSwipeItem.BackgroundColor.ToBrush();
+
+ var textColor = GetSwipeItemColor(formsSwipeItem.BackgroundColor);
+ windowsSwipeItem.Foreground = textColor.ToBrush();
+
+ windowsSwipeItem.Command = formsSwipeItem.Command;
+ windowsSwipeItem.CommandParameter = formsSwipeItem.CommandParameter;
+ }
+ }
+
+ bool IsValidSwipeItems(SwipeItems swipeItems)
+ {
+ return swipeItems != null && swipeItems.Count > 0;
+ }
+
+ void DisposeSwipeItems(Dictionary<WSwipeItem, SwipeItem> list)
+ {
+ if (list != null)
+ {
+ foreach (var item in list)
+ {
+ if (item.Key != null)
+ item.Key.Invoked -= OnSwipeItemInvoked;
+ if (item.Value != null)
+ item.Value.PropertyChanged -= OnSwipeItemPropertyChanged;
+ }
+
+ list.Clear();
+ list = null;
+ }
+ }
+
+ WSwipeItems CreateSwipeItems(SwipeDirection swipeDirection)
+ {
+ var swipeItems = new WSwipeItems();
+
+ SwipeItems items = null;
+
+ switch (swipeDirection)
+ {
+ case SwipeDirection.Left:
+ DisposeSwipeItems(_leftItems);
+ items = Element.LeftItems;
+ _leftItems = new Dictionary<WSwipeItem, SwipeItem>();
+ break;
+ case SwipeDirection.Right:
+ DisposeSwipeItems(_rightItems);
+ items = Element.RightItems;
+ _rightItems = new Dictionary<WSwipeItem, SwipeItem>();
+ break;
+ case SwipeDirection.Up:
+ DisposeSwipeItems(_topItems);
+ items = Element.TopItems;
+ _topItems = new Dictionary<WSwipeItem, SwipeItem>();
+ break;
+ case SwipeDirection.Down:
+ DisposeSwipeItems(_bottomItems);
+ items = Element.BottomItems;
+ _bottomItems = new Dictionary<WSwipeItem, SwipeItem>();
+ break;
+ }
+
+ items.PropertyChanged += OnSwipeItemsPropertyChanged;
+ swipeItems.Mode = GetSwipeMode(items.Mode);
+
+ foreach (var item in items)
+ {
+ if (item is SwipeItem formsSwipeItem)
+ {
+ var textColor = GetSwipeItemColor(formsSwipeItem.BackgroundColor);
+
+ var windowsSwipeItem = new WSwipeItem
+ {
+ Background = formsSwipeItem.BackgroundColor.IsDefault ? null : formsSwipeItem.BackgroundColor.ToBrush(),
+ Foreground = textColor.ToBrush(),
+ IconSource = formsSwipeItem.IconImageSource.ToWindowsIconSource(),
+ Text = formsSwipeItem.Text,
+ Command = formsSwipeItem.Command,
+ CommandParameter = formsSwipeItem.CommandParameter,
+ BehaviorOnInvoked = GetSwipeBehaviorOnInvoked(items.SwipeBehaviorOnInvoked)
+ };
+
+ formsSwipeItem.PropertyChanged += OnSwipeItemPropertyChanged;
+ windowsSwipeItem.Invoked += OnSwipeItemInvoked;
+
+ swipeItems.Add(windowsSwipeItem);
+
+ FillSwipeItemsCache(swipeDirection, windowsSwipeItem, formsSwipeItem);
+ }
+ }
+
+ return swipeItems;
+ }
+
+ void FillSwipeItemsCache(SwipeDirection swipeDirection, WSwipeItem windowsSwipeItem, SwipeItem formsSwipeItem)
+ {
+ switch (swipeDirection)
+ {
+ case SwipeDirection.Left:
+ _leftItems.Add(windowsSwipeItem, formsSwipeItem);
+ break;
+ case SwipeDirection.Right:
+ _rightItems.Add(windowsSwipeItem, formsSwipeItem);
+ break;
+ case SwipeDirection.Up:
+ _topItems.Add(windowsSwipeItem, formsSwipeItem);
+ break;
+ case SwipeDirection.Down:
+ _bottomItems.Add(windowsSwipeItem, formsSwipeItem);
+ break;
+ }
+ }
+
+ void OnSwipeItemInvoked(WSwipeItem sender, Microsoft.UI.Xaml.Controls.SwipeItemInvokedEventArgs args)
+ {
+ var windowsSwipeItem = sender;
+ var formsSwipeItem = GetFormsSwipeItem(windowsSwipeItem);
+ formsSwipeItem?.OnInvoked();
+ }
+
+ WSwipeItems GetWindowsSwipeItems(SwipeItems swipeItems)
+ {
+ if (swipeItems == Element.LeftItems)
+ return Control.LeftItems;
+
+ if (swipeItems == Element.RightItems)
+ return Control.RightItems;
+
+ if (swipeItems == Element.TopItems)
+ return Control.TopItems;
+
+ if (swipeItems == Element.BottomItems)
+ return Control.BottomItems;
+
+ return null;
+ }
+
+ WSwipeItem GetWindowsSwipeItem(SwipeItem swipeItem)
+ {
+ if (_leftItems != null)
+ return _leftItems.FirstOrDefault(x => x.Value.Equals(swipeItem)).Key;
+
+ if (_rightItems != null)
+ return _rightItems.FirstOrDefault(x => x.Value.Equals(swipeItem)).Key;
+
+ if (_topItems != null)
+ return _topItems.FirstOrDefault(x => x.Value.Equals(swipeItem)).Key;
+
+ if (_bottomItems != null)
+ return _bottomItems.FirstOrDefault(x => x.Value.Equals(swipeItem)).Key;
+
+ return null;
+ }
+
+ SwipeItem GetFormsSwipeItem(WSwipeItem swipeItem)
+ {
+ if (_leftItems != null)
+ {
+ _leftItems.TryGetValue(swipeItem, out SwipeItem formsSwipeItem);
+
+ if (formsSwipeItem != null)
+ return formsSwipeItem;
+ }
+
+ if (_rightItems != null)
+ {
+ _rightItems.TryGetValue(swipeItem, out SwipeItem formsSwipeItem);
+
+ if (formsSwipeItem != null)
+ return formsSwipeItem;
+ }
+
+ if (_topItems != null)
+ {
+ _topItems.TryGetValue(swipeItem, out SwipeItem formsSwipeItem);
+
+ if (formsSwipeItem != null)
+ return formsSwipeItem;
+ }
+
+ if (_bottomItems != null)
+ {
+ _bottomItems.TryGetValue(swipeItem, out SwipeItem formsSwipeItem);
+
+ if (formsSwipeItem != null)
+ return formsSwipeItem;
+ }
+
+ return null;
+ }
+
+ WSwipeMode GetSwipeMode(SwipeMode swipeMode)
+ {
+ switch (swipeMode)
+ {
+ case SwipeMode.Execute:
+ return WSwipeMode.Execute;
+ case SwipeMode.Reveal:
+ return WSwipeMode.Reveal;
+ }
+
+ return WSwipeMode.Reveal;
+ }
+
+ WSwipeBehaviorOnInvoked GetSwipeBehaviorOnInvoked(SwipeBehaviorOnInvoked swipeBehaviorOnInvoked)
+ {
+ switch (swipeBehaviorOnInvoked)
+ {
+ case SwipeBehaviorOnInvoked.Auto:
+ return WSwipeBehaviorOnInvoked.Auto;
+ case SwipeBehaviorOnInvoked.Close:
+ return WSwipeBehaviorOnInvoked.Close;
+ case SwipeBehaviorOnInvoked.RemainOpen:
+ return WSwipeBehaviorOnInvoked.RemainOpen;
+ }
+
+ return WSwipeBehaviorOnInvoked.Auto;
+ }
+
+ Color GetSwipeItemColor(Color backgroundColor)
+ {
+ var luminosity = 0.2126 * backgroundColor.R + 0.7152 * backgroundColor.G + 0.0722 * backgroundColor.B;
+
+ return luminosity < 0.75 ? Color.White : Color.Black;
+ }
+
+ void OnCloseRequested(object sender, EventArgs e)
+ {
+ if (Control == null)
+ return;
+
+ Control.Close();
+ }
+ }
+}
\ No newline at end of file
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage.Streams;
+using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Platform.UWP
{
- public sealed class UriImageSourceHandler : IImageSourceHandler
+ public sealed class UriImageSourceHandler : IImageSourceHandler, IIconElementHandler
{
+ public Task<IconElement> LoadIconElementAsync(ImageSource imagesource, CancellationToken cancellationToken = default)
+ {
+ var imageLoader = imagesource as UriImageSource;
+
+ if (imageLoader?.Uri == null)
+ return null;
+
+ IconElement image = new BitmapIcon { UriSource = imageLoader?.Uri };
+
+ return Task.FromResult(image);
+ }
+
+ public Task<Microsoft.UI.Xaml.Controls.IconSource> LoadIconSourceAsync(ImageSource imagesource, CancellationToken cancellationToken = default)
+ {
+ var imageLoader = imagesource as UriImageSource;
+
+ if (imageLoader?.Uri == null)
+ return null;
+
+ Microsoft.UI.Xaml.Controls.IconSource image = new Microsoft.UI.Xaml.Controls.BitmapIconSource { UriSource = imageLoader?.Uri };
+
+ return Task.FromResult(image);
+ }
+
public async Task<Windows.UI.Xaml.Media.ImageSource> LoadImageAsync(ImageSource imagesource, CancellationToken cancellationToken = new CancellationToken())
{
var imageLoader = imagesource as UriImageSource;
+
if (imageLoader?.Uri == null)
return null;
Stream streamImage = await imageLoader.GetStreamAsync(cancellationToken);
+
if (streamImage == null || !streamImage.CanRead)
{
return null;
<DependentUpon>StepperControl.xaml</DependentUpon>
</Compile>
<Compile Include="CheckBoxRenderer.cs" />
+ <Compile Include="SwipeViewRenderer.cs" />
<Compile Include="TextBlockExtensions.cs" />
<Compile Include="TitleViewManager.cs" />
<Compile Include="UriImageSourceHandler.cs" />
[assembly: ExportRenderer(typeof(WebView), typeof(WebViewRenderer))]
[assembly: ExportRenderer(typeof(SearchBar), typeof(SearchBarRenderer))]
[assembly: ExportRenderer(typeof(Switch), typeof(SwitchRenderer))]
+[assembly: ExportRenderer(typeof(SwipeView), typeof(SwipeViewRenderer))]
[assembly: ExportRenderer(typeof(DatePicker), typeof(DatePickerRenderer))]
[assembly: ExportRenderer(typeof(TimePicker), typeof(TimePickerRenderer))]
[assembly: ExportRenderer(typeof(Picker), typeof(PickerRenderer))]
{
var args = new ScrolledEventArgs(scrollView.ContentOffset.X, scrollView.ContentOffset.Y);
List?.SendScrolled(args);
+
if (_isDragging && scrollView.ContentOffset.Y < 0)
{
// If the refresh spinner is currently displayed and pull-to-refresh is not enabled,
return;
}
- UITouchEventArgs shouldReceive = (g, t) => !(t.View is UISlider);
+ bool shouldReceive(UIGestureRecognizer g, UITouch t)
+ {
+ return !(t.View is UISlider) && !(IsSwipeView(t.View));
+ }
+
var center = new PointF();
_panGesture = new UIPanGestureRecognizer(g =>
{
break;
}
});
+ _panGesture.CancelsTouchesInView = false;
_panGesture.ShouldReceiveTouch = shouldReceive;
_panGesture.MaximumNumberOfTouches = 2;
View.AddGestureRecognizer(_panGesture);
}
+ bool IsSwipeView(UIView view)
+ {
+ if (view == null)
+ return false;
+
+ if (view.Superview is SwipeViewRenderer)
+ return true;
+
+ return IsSwipeView(view.Superview);
+ }
+
class ChildViewController : UIViewController
{
public override void ViewDidLayoutSubviews()
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using CoreGraphics;
+using Foundation;
+using UIKit;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
+using Specifics = Xamarin.Forms.PlatformConfiguration.iOSSpecific.SwipeView;
+
+namespace Xamarin.Forms.Platform.iOS
+{
+ public class SwipeViewRenderer : ViewRenderer<SwipeView, UIView>
+ {
+ const double SwipeThreshold = 250;
+ const int SwipeThresholdMargin = 6;
+ const double SwipeItemWidth = 100;
+ const double SwipeAnimationDuration = 0.2;
+
+ View _scrollParent;
+ UIView _contentView;
+ UIStackView _actionView;
+ UITapGestureRecognizer _tapGestureRecognizer;
+ SwipeTransitionMode _swipeTransitionMode;
+ SwipeDirection? _swipeDirection;
+ CGPoint _initialPoint;
+ bool _isTouchDown;
+ bool _isSwiping;
+ double _swipeOffset;
+ double _swipeThreshold;
+ CGRect _originalBounds;
+ List<CGRect> _swipeItemsRect;
+ bool _isDisposed;
+
+ public SwipeViewRenderer()
+ {
+ _tapGestureRecognizer = new UITapGestureRecognizer(OnTap)
+ {
+ CancelsTouchesInView = true
+ };
+ AddGestureRecognizer(_tapGestureRecognizer);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<SwipeView> e)
+ {
+ if (e.NewElement != null)
+ {
+ e.NewElement.CloseRequested += OnCloseRequested;
+
+ if (Control == null)
+ {
+ SetNativeControl(CreateNativeControl());
+ }
+
+ UpdateContent();
+ UpdateSwipeTransitionMode();
+ SetBackgroundColor(Element.BackgroundColor);
+ }
+
+ if (e.OldElement != null)
+ e.OldElement.CloseRequested -= OnCloseRequested;
+
+ base.OnElementChanged(e);
+ }
+
+ protected override UIView CreateNativeControl()
+ {
+ return new UIView();
+ }
+
+ public override void WillMoveToWindow(UIWindow window)
+ {
+ base.WillMoveToWindow(window);
+
+ if (window != null && _scrollParent == null)
+ {
+ _scrollParent = Element.FindParentOfType<ScrollView>();
+
+ if (_scrollParent is ScrollView scrollView)
+ {
+ scrollView.Scrolled += OnParentScrolled;
+ return;
+ }
+
+ _scrollParent = Element.FindParentOfType<ListView>();
+
+ if (_scrollParent is ListView listView)
+ {
+ listView.Scrolled += OnParentScrolled;
+ return;
+ }
+
+ _scrollParent = Element.FindParentOfType<CollectionView>();
+
+ if (_scrollParent is CollectionView collectionView)
+ {
+ collectionView.Scrolled += OnParentScrolled;
+ }
+ }
+ }
+
+ public override void LayoutSubviews()
+ {
+ base.LayoutSubviews();
+
+ if (Element.Content != null)
+ Element.Content.Layout(Bounds.ToRectangle());
+
+ if (_contentView != null)
+ _contentView.Frame = Bounds;
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == ContentView.ContentProperty.PropertyName)
+ UpdateContent();
+ else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ SetBackgroundColor(Element.BackgroundColor);
+ else if (e.PropertyName == Specifics.SwipeTransitionModeProperty.PropertyName)
+ UpdateSwipeTransitionMode();
+ }
+
+ protected override void SetBackgroundColor(Color color)
+ {
+ if (Element.BackgroundColor != Color.Default)
+ {
+ BackgroundColor = Element.BackgroundColor.ToUIColor();
+
+ if (_contentView != null && Element.Content == null && HasSwipeItems())
+ _contentView.BackgroundColor = Element.BackgroundColor.ToUIColor();
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ return;
+
+ if (disposing)
+ {
+ if (Element != null)
+ {
+ Element.CloseRequested -= OnCloseRequested;
+ }
+
+ if (_scrollParent != null)
+ {
+ if(_scrollParent is ScrollView scrollView)
+ scrollView.Scrolled -= OnParentScrolled;
+
+ if (_scrollParent is ListView listView)
+ listView.Scrolled -= OnParentScrolled;
+
+ if (_scrollParent is CollectionView collectionView)
+ collectionView.Scrolled -= OnParentScrolled;
+ }
+
+ if (_tapGestureRecognizer != null)
+ {
+ Control.RemoveGestureRecognizer(_tapGestureRecognizer);
+ _tapGestureRecognizer.Dispose();
+ _tapGestureRecognizer = null;
+ }
+
+ if (_contentView != null)
+ {
+ _contentView.Dispose();
+ _contentView = null;
+ }
+
+ if (_actionView != null)
+ {
+ _actionView.Dispose();
+ _actionView = null;
+ }
+
+ if (_swipeItemsRect != null)
+ {
+ _swipeItemsRect.Clear();
+ _swipeItemsRect = null;
+ }
+ }
+
+ _isDisposed = true;
+
+ base.Dispose(disposing);
+ }
+
+ public override void TouchesBegan(NSSet touches, UIEvent evt)
+ {
+ var navigationController = GetUINavigationController(GetViewController());
+
+ if (navigationController != null)
+ navigationController.InteractivePopGestureRecognizer.Enabled = false;
+
+ HandleTouchInteractions(touches, GestureStatus.Started);
+
+ base.TouchesBegan(touches, evt);
+ }
+
+ public override void TouchesMoved(NSSet touches, UIEvent evt)
+ {
+ HandleTouchInteractions(touches, GestureStatus.Running);
+ base.TouchesMoved(touches, evt);
+ }
+
+ public override void TouchesEnded(NSSet touches, UIEvent evt)
+ {
+ var navigationController = GetUINavigationController(GetViewController());
+
+ if (navigationController != null)
+ navigationController.InteractivePopGestureRecognizer.Enabled = true;
+
+ HandleTouchInteractions(touches, GestureStatus.Completed);
+
+ base.TouchesEnded(touches, evt);
+ }
+
+ public override void TouchesCancelled(NSSet touches, UIEvent evt)
+ {
+ var navigationController = GetUINavigationController(GetViewController());
+
+ if (navigationController != null)
+ navigationController.InteractivePopGestureRecognizer.Enabled = true;
+
+ HandleTouchInteractions(touches, GestureStatus.Canceled);
+
+ base.TouchesCancelled(touches, evt);
+ }
+
+ void UpdateContent()
+ {
+ ClipsToBounds = true;
+
+ if (Element.Content == null)
+ _contentView = CreateEmptyContent();
+ else
+ _contentView = CreateContent();
+
+ AddSubview(_contentView);
+ }
+
+ UIView CreateEmptyContent()
+ {
+ var emptyContentView = new UIView
+ {
+ BackgroundColor = Color.Default.ToUIColor()
+ };
+
+ return emptyContentView;
+ }
+
+ UIView CreateContent()
+ {
+ var formsElement = Element.Content;
+ var renderer = Platform.CreateRenderer(formsElement);
+ Platform.SetRenderer(formsElement, renderer);
+
+ return renderer?.NativeView;
+ }
+
+ bool HasSwipeItems()
+ {
+ return Element != null && (IsValidSwipeItems(Element.LeftItems) || IsValidSwipeItems(Element.RightItems) || IsValidSwipeItems(Element.TopItems) || IsValidSwipeItems(Element.BottomItems));
+ }
+
+ bool IsHorizontalSwipe()
+ {
+ return _swipeDirection == SwipeDirection.Left || _swipeDirection == SwipeDirection.Right;
+ }
+
+ bool IsValidSwipeItems(SwipeItems swipeItems)
+ {
+ return swipeItems != null && swipeItems.Count > 0;
+ }
+
+ void UpdateSwipeItems()
+ {
+ if (_contentView == null || _actionView != null)
+ return;
+
+ _swipeItemsRect = new List<CGRect>();
+
+ SwipeItems items = GetSwipeItemsByDirection();
+ double swipeItemsWidth;
+
+ if (_swipeDirection == SwipeDirection.Left || _swipeDirection == SwipeDirection.Right)
+ swipeItemsWidth = (items != null ? items.Count : 0) * SwipeItemWidth;
+ else
+ swipeItemsWidth = _contentView.Frame.Width;
+
+ if (items == null)
+ return;
+
+ _actionView = new UIStackView
+ {
+ Axis = UILayoutConstraintAxis.Horizontal,
+ Frame = new CGRect(0, 0, swipeItemsWidth, _contentView.Frame.Height)
+ };
+
+ int i = 0;
+ float previousWidth = 0;
+
+ foreach (var item in items)
+ {
+ var swipeItem = (item is SwipeItem) ? CreateSwipeItem((SwipeItem)item) : CreateSwipeItemView((SwipeItemView)item);
+
+ var swipeItemSize = GetSwipeItemSize(item);
+ float swipeItemHeight = (float)swipeItemSize.Height;
+ float swipeItemWidth = (float)swipeItemSize.Width;
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ swipeItem.Frame = new CGRect(_contentView.Frame.Width - (swipeItemWidth + previousWidth), 0, i + 1 * swipeItemWidth, swipeItemHeight);
+ break;
+ case SwipeDirection.Right:
+ case SwipeDirection.Up:
+ case SwipeDirection.Down:
+ swipeItem.Frame = new CGRect(previousWidth, 0, i + 1 * swipeItemWidth, swipeItemHeight);
+ break;
+ }
+
+ _actionView.AddSubview(swipeItem);
+ _swipeItemsRect.Add(swipeItem.Frame);
+ previousWidth += swipeItemWidth;
+
+ i++;
+ }
+
+ AddSubview(_actionView);
+ BringSubviewToFront(_contentView);
+ }
+
+ UIButton CreateSwipeItem(SwipeItem formsSwipeItem)
+ {
+ var swipeItem = new UIButton(UIButtonType.Custom)
+ {
+ BackgroundColor = formsSwipeItem.BackgroundColor.ToUIColor()
+ };
+
+ swipeItem.SetTitle(formsSwipeItem.Text, UIControlState.Normal);
+
+ UpdateSwipeItemIconImage(swipeItem, formsSwipeItem);
+
+ var textColor = GetSwipeItemColor(formsSwipeItem.BackgroundColor);
+ swipeItem.SetTitleColor(textColor.ToUIColor(), UIControlState.Normal);
+ swipeItem.UserInteractionEnabled = false;
+ UpdateSwipeItemInsets(swipeItem);
+
+ return swipeItem;
+ }
+
+ UIView CreateSwipeItemView(SwipeItemView formsSwipeItemView)
+ {
+ var renderer = Platform.CreateRenderer(formsSwipeItemView);
+ Platform.SetRenderer(formsSwipeItemView, renderer);
+ UpdateSwipeItemViewLayout(formsSwipeItemView);
+ return renderer?.NativeView;
+ }
+
+ void UpdateSwipeItemViewLayout(SwipeItemView swipeItemView)
+ {
+ var swipeItemSize = GetSwipeItemSize(swipeItemView);
+
+ swipeItemView.Layout(new Rectangle(0, 0, swipeItemSize.Width, swipeItemSize.Height));
+ }
+
+ void UpdateSwipeTransitionMode()
+ {
+ if (Element.IsSet(Specifics.SwipeTransitionModeProperty))
+ _swipeTransitionMode = Element.OnThisPlatform().GetSwipeTransitionMode();
+ else
+ _swipeTransitionMode = SwipeTransitionMode.Reveal;
+ }
+
+ void UpdateSwipeItemInsets(UIButton button, float spacing = 0.0f)
+ {
+ if (button.ImageView?.Image == null)
+ return;
+
+ var imageSize = button.ImageView.Image.Size;
+
+ var titleEdgeInsets = new UIEdgeInsets(spacing, -imageSize.Width, -imageSize.Height, 0.0f);
+ button.TitleEdgeInsets = titleEdgeInsets;
+
+ var labelString = button.TitleLabel.Text;
+ var titleSize = labelString.StringSize(button.TitleLabel.Font);
+ var imageEdgeInsets = new UIEdgeInsets(-(titleSize.Height + spacing), 0.0f, 0.0f, -titleSize.Width);
+ button.ImageEdgeInsets = imageEdgeInsets;
+ }
+
+ Color GetSwipeItemColor(Color backgroundColor)
+ {
+ var luminosity = 0.2126 * backgroundColor.R + 0.7152 * backgroundColor.G + 0.0722 * backgroundColor.B;
+
+ return luminosity < 0.75 ? Color.White : Color.Black;
+ }
+
+ async void UpdateSwipeItemIconImage(UIButton swipeButton, SwipeItem swipeItem)
+ {
+ if (swipeButton == null)
+ return;
+
+ if (swipeItem.IconImageSource == null || swipeItem.IconImageSource.IsEmpty)
+ {
+ swipeButton.SetImage(null, UIControlState.Normal);
+ }
+ else
+ {
+ var image = await swipeItem.IconImageSource.GetNativeImageAsync();
+
+ try
+ {
+ swipeButton.SetImage(image.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate), UIControlState.Normal);
+ var tintColor = GetSwipeItemColor(swipeItem.BackgroundColor);
+ swipeButton.TintColor = tintColor.ToUIColor();
+ }
+ catch (Exception)
+ {
+ // UIImage ctor throws on file not found if MonoTouch.ObjCRuntime.Class.ThrowOnInitFailure is true;
+ Log.Warning("SwipeView", "Can not load SwipeItem Icon.");
+ }
+ }
+ }
+
+ void OnTap()
+ {
+ var state = _tapGestureRecognizer.State;
+
+ if (state != UIGestureRecognizerState.Cancelled)
+ {
+ if (_contentView == null)
+ return;
+
+ var point = _tapGestureRecognizer.LocationInView(this);
+
+ bool touchContent = TouchInsideContent(_contentView.Frame.X, _contentView.Frame.Y, _contentView.Frame.Width, _contentView.Frame.Height, point.X, point.Y);
+
+ if (!touchContent)
+ ProcessTouchSwipeItems(point);
+ else
+ ResetSwipe();
+ }
+ }
+
+ void HandleTouchInteractions(NSSet touches, GestureStatus gestureStatus)
+ {
+ var anyObject = touches.AnyObject as UITouch;
+ nfloat x = anyObject.LocationInView(this).X;
+ nfloat y = anyObject.LocationInView(this).Y;
+
+ HandleTouchInteractions(gestureStatus, new CGPoint(x, y));
+ }
+
+ void HandleTouchInteractions(GestureStatus status, CGPoint point)
+ {
+ switch (status)
+ {
+ case GestureStatus.Started:
+ ProcessTouchDown(point);
+ break;
+ case GestureStatus.Running:
+ ProcessTouchMove(point);
+ break;
+ case GestureStatus.Canceled:
+ case GestureStatus.Completed:
+ ProcessTouchUp();
+ break;
+ }
+
+ _isTouchDown = false;
+ }
+
+ void ProcessTouchDown(CGPoint point)
+ {
+ if (_isSwiping || _isTouchDown || _contentView == null)
+ return;
+
+ _initialPoint = point;
+ _isTouchDown = true;
+ }
+
+ bool TouchInsideContent(double x1, double y1, double x2, double y2, double x, double y)
+ {
+ if (x > x1 && x < (x1 + x2) && y > y1 && y < (y1 + y2))
+ return true;
+
+ return false;
+ }
+
+ void ProcessTouchMove(CGPoint point)
+ {
+ if (!_isSwiping)
+ {
+ _swipeDirection = SwipeDirectionHelper.GetSwipeDirection(new Point(_initialPoint.X, _initialPoint.Y), new Point(point.X, point.Y));
+ RaiseSwipeStarted();
+ _isSwiping = true;
+ }
+
+ if (!ValidateSwipeDirection())
+ return;
+
+ _swipeOffset = GetSwipeOffset(_initialPoint, point);
+
+ UpdateSwipeItems();
+
+ if (Math.Abs(_swipeOffset) > double.Epsilon)
+ Swipe();
+ else
+ ResetSwipe();
+
+ RaiseSwipeChanging();
+ }
+
+ void ProcessTouchUp()
+ {
+ _isTouchDown = false;
+ _isSwiping = false;
+ RaiseSwipeEnded();
+
+ if (!ValidateSwipeDirection())
+ return;
+
+ ValidateSwipeThreshold();
+ }
+
+ SwipeItems GetSwipeItemsByDirection()
+ {
+ SwipeItems swipeItems = null;
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ swipeItems = Element.RightItems;
+ break;
+ case SwipeDirection.Right:
+ swipeItems = Element.LeftItems;
+ break;
+ case SwipeDirection.Up:
+ swipeItems = Element.BottomItems;
+ break;
+ case SwipeDirection.Down:
+ swipeItems = Element.TopItems;
+ break;
+ }
+
+ return swipeItems;
+ }
+
+ void Swipe()
+ {
+ if (_contentView == null)
+ return;
+
+ _originalBounds = _contentView.Bounds;
+
+ if (_swipeTransitionMode == SwipeTransitionMode.Reveal)
+ {
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ _contentView.Frame = new CGRect(_originalBounds.X + ValidateSwipeOffset(_swipeOffset), _originalBounds.Y, _originalBounds.Width, _originalBounds.Height);
+ break;
+ case SwipeDirection.Right:
+ _contentView.Frame = new CGRect(_originalBounds.X + ValidateSwipeOffset(_swipeOffset), _originalBounds.Y, _originalBounds.Width, _originalBounds.Height);
+ break;
+ case SwipeDirection.Up:
+ _contentView.Frame = new CGRect(_originalBounds.X, _originalBounds.Y + ValidateSwipeOffset(_swipeOffset), _originalBounds.Width, _originalBounds.Height);
+ break;
+ case SwipeDirection.Down:
+ _contentView.Frame = new CGRect(_originalBounds.X, _originalBounds.Y + ValidateSwipeOffset(_swipeOffset), _originalBounds.Width, _originalBounds.Height);
+ break;
+ }
+ }
+
+ if (_swipeTransitionMode == SwipeTransitionMode.Drag)
+ {
+ var actionBounds = _actionView.Bounds;
+ double actionSize;
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ _contentView.Frame = new CGRect(_originalBounds.X + ValidateSwipeOffset(_swipeOffset), _originalBounds.Y, _originalBounds.Width, _originalBounds.Height);
+ actionSize = Element.RightItems.Count * SwipeItemWidth;
+ _actionView.Frame = new CGRect(actionSize + ValidateSwipeOffset(_swipeOffset), actionBounds.Y, actionBounds.Width, actionBounds.Height);
+ break;
+ case SwipeDirection.Right:
+ _contentView.Frame = new CGRect(_originalBounds.X + ValidateSwipeOffset(_swipeOffset), _originalBounds.Y, _originalBounds.Width, _originalBounds.Height);
+ actionSize = Element.LeftItems.Count * SwipeItemWidth;
+ _actionView.Frame = new CGRect(-actionSize + ValidateSwipeOffset(_swipeOffset), actionBounds.Y, actionBounds.Width, actionBounds.Height);
+ break;
+ case SwipeDirection.Up:
+ _contentView.Frame = new CGRect(_originalBounds.X, _originalBounds.Y + ValidateSwipeOffset(_swipeOffset), _originalBounds.Width, _originalBounds.Height);
+ actionSize = _contentView.Frame.Height;
+ _actionView.Frame = new CGRect(actionBounds.X, actionSize - Math.Abs(ValidateSwipeOffset(_swipeOffset)), actionBounds.Width, actionBounds.Height);
+ break;
+ case SwipeDirection.Down:
+ _contentView.Frame = new CGRect(_originalBounds.X, _originalBounds.Y + ValidateSwipeOffset(_swipeOffset), _originalBounds.Width, _originalBounds.Height);
+ actionSize = _contentView.Frame.Height;
+ _actionView.Frame = new CGRect(actionBounds.X, -actionSize + Math.Abs(ValidateSwipeOffset(_swipeOffset)), actionBounds.Width, actionBounds.Height);
+ break;
+ }
+ }
+ }
+
+ double ValidateSwipeOffset(double offset)
+ {
+ var swipeThreshold = GetSwipeThreshold();
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ if (offset > 0)
+ offset = 0;
+
+ if (Math.Abs(offset) > swipeThreshold)
+ return -swipeThreshold;
+ break;
+ case SwipeDirection.Right:
+ if (offset < 0)
+ offset = 0;
+
+ if (Math.Abs(offset) > swipeThreshold)
+ return swipeThreshold;
+ break;
+ case SwipeDirection.Up:
+ if (offset > 0)
+ offset = 0;
+
+ if (Math.Abs(offset) > swipeThreshold)
+ return -swipeThreshold;
+ break;
+ case SwipeDirection.Down:
+ if (offset < 0)
+ offset = 0;
+
+ if (Math.Abs(offset) > swipeThreshold)
+ return swipeThreshold;
+ break;
+ }
+
+ return offset;
+ }
+
+ void DisposeSwipeItems()
+ {
+ if (_actionView != null)
+ {
+ _actionView.RemoveFromSuperview();
+ _actionView.Dispose();
+ _actionView = null;
+ }
+
+ if (_swipeItemsRect != null)
+ {
+ _swipeItemsRect.Clear();
+ _swipeItemsRect = null;
+ }
+ }
+
+ private void ResetSwipe()
+ {
+ if (_swipeItemsRect == null)
+ return;
+
+ if (_contentView != null)
+ {
+ _isSwiping = false;
+ _swipeThreshold = 0;
+ _swipeDirection = null;
+
+ Animate(SwipeAnimationDuration, 0.0, UIViewAnimationOptions.CurveEaseOut, () =>
+ {
+ _contentView.Frame = new CGRect(_originalBounds.X, _originalBounds.Y, _originalBounds.Width, _originalBounds.Height);
+ },
+ DisposeSwipeItems);
+ }
+ }
+
+ void ValidateSwipeThreshold()
+ {
+ if (_swipeDirection == null)
+ return;
+
+ var swipeThresholdPercent = 0.6 * GetSwipeThreshold();
+
+ if (Math.Abs(_swipeOffset) >= swipeThresholdPercent)
+ {
+ var swipeItems = GetSwipeItemsByDirection();
+
+ if (swipeItems == null)
+ return;
+
+ if (swipeItems.Mode == SwipeMode.Execute)
+ {
+ foreach (var swipeItem in swipeItems)
+ {
+ ExecuteSwipeItem(swipeItem);
+ }
+
+ if (swipeItems.SwipeBehaviorOnInvoked != SwipeBehaviorOnInvoked.RemainOpen)
+ ResetSwipe();
+ }
+ else
+ CompleteSwipe();
+ }
+ else
+ {
+ ResetSwipe();
+ }
+ }
+
+ void CompleteSwipe()
+ {
+ if (_swipeTransitionMode == SwipeTransitionMode.Reveal)
+ {
+ Animate(SwipeAnimationDuration, 0.0, UIViewAnimationOptions.CurveEaseIn,
+ () =>
+ {
+ double swipeThreshold = GetSwipeThreshold();
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ _contentView.Frame = new CGRect(_originalBounds.X - swipeThreshold, _originalBounds.Y, _originalBounds.Width, _originalBounds.Height);
+ break;
+ case SwipeDirection.Right:
+ _contentView.Frame = new CGRect(_originalBounds.X + swipeThreshold, _originalBounds.Y, _originalBounds.Width, _originalBounds.Height);
+ break;
+ case SwipeDirection.Up:
+ _contentView.Frame = new CGRect(_originalBounds.X, _originalBounds.Y - swipeThreshold, _originalBounds.Width, _originalBounds.Height);
+ break;
+ case SwipeDirection.Down:
+ _contentView.Frame = new CGRect(_originalBounds.X, _originalBounds.Y + swipeThreshold, _originalBounds.Width, _originalBounds.Height);
+ break;
+ }
+ },
+ () =>
+ {
+ _isSwiping = false;
+ });
+ }
+
+ if (_swipeTransitionMode == SwipeTransitionMode.Drag)
+ {
+ Animate(SwipeAnimationDuration, 0.0, UIViewAnimationOptions.CurveEaseIn,
+ () =>
+ {
+ double swipeThreshold = GetSwipeThreshold();
+ var actionBounds = _actionView.Bounds;
+ double actionSize;
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ _contentView.Frame = new CGRect(_originalBounds.X - swipeThreshold, _originalBounds.Y, _originalBounds.Width, _originalBounds.Height);
+ actionSize = Element.RightItems.Count * SwipeItemWidth;
+ _actionView.Frame = new CGRect(actionSize - swipeThreshold, actionBounds.Y, actionBounds.Width, actionBounds.Height);
+ break;
+ case SwipeDirection.Right:
+ _contentView.Frame = new CGRect(_originalBounds.X + swipeThreshold, _originalBounds.Y, _originalBounds.Width, _originalBounds.Height);
+ actionSize = Element.LeftItems.Count * SwipeItemWidth;
+ _actionView.Frame = new CGRect(-actionSize + swipeThreshold, actionBounds.Y, actionBounds.Width, actionBounds.Height);
+ break;
+ case SwipeDirection.Up:
+ _contentView.Frame = new CGRect(_originalBounds.X, _originalBounds.Y - swipeThreshold, _originalBounds.Width, _originalBounds.Height);
+ actionSize = _contentView.Frame.Height;
+ _actionView.Frame = new CGRect(actionBounds.X, actionSize - Math.Abs(swipeThreshold), actionBounds.Width, actionBounds.Height);
+ break;
+ case SwipeDirection.Down:
+ _contentView.Frame = new CGRect(_originalBounds.X, _originalBounds.Y + swipeThreshold, _originalBounds.Width, _originalBounds.Height);
+ actionSize = _contentView.Frame.Height;
+ _actionView.Frame = new CGRect(actionBounds.X, -actionSize + Math.Abs(swipeThreshold), actionBounds.Width, actionBounds.Height);
+ break;
+ }
+ },
+ () =>
+ {
+ _isSwiping = false;
+ });
+ }
+
+ }
+
+ double GetSwipeThreshold()
+ {
+ if (Math.Abs(_swipeThreshold) > double.Epsilon)
+ return _swipeThreshold;
+
+ var swipeItems = GetSwipeItemsByDirection();
+
+ if (swipeItems == null)
+ return 0;
+
+ _swipeThreshold = GetSwipeThreshold(swipeItems);
+
+ return _swipeThreshold;
+ }
+
+ double GetSwipeThreshold(SwipeItems swipeItems)
+ {
+ double swipeThreshold = 0;
+
+ bool isHorizontal = IsHorizontalSwipe();
+
+ if (swipeItems.Mode == SwipeMode.Reveal)
+ {
+ if (isHorizontal)
+ {
+ foreach (var swipeItem in swipeItems)
+ {
+ var swipeItemSize = GetSwipeItemSize(swipeItem);
+ swipeThreshold += swipeItemSize.Width;
+ }
+ }
+ else
+ swipeThreshold = GetSwipeItemHeight();
+ }
+ else
+ {
+ if (isHorizontal)
+ swipeThreshold = SwipeThreshold;
+ else
+ {
+ var contentHeight = _contentView.Frame.Height;
+ swipeThreshold = (SwipeThreshold > contentHeight) ? contentHeight : SwipeThreshold;
+ }
+ }
+
+ return ValidateSwipeThreshold(swipeThreshold);
+ }
+
+ double ValidateSwipeThreshold(double swipeThreshold)
+ {
+ if (IsHorizontalSwipe())
+ {
+ if (swipeThreshold > _contentView.Frame.Width)
+ swipeThreshold = _contentView.Frame.Width;
+
+ return swipeThreshold - SwipeThresholdMargin;
+ }
+
+ if (swipeThreshold > _contentView.Frame.Height)
+ swipeThreshold = _contentView.Frame.Height;
+
+ return swipeThreshold - SwipeThresholdMargin / 2;
+ }
+
+ Size GetSwipeItemSize(ISwipeItem swipeItem)
+ {
+ var items = GetSwipeItemsByDirection();
+
+ if (IsHorizontalSwipe())
+ {
+ if (swipeItem is SwipeItem)
+ return new Size(items.Mode == SwipeMode.Execute ? _contentView.Frame.Width / items.Count : SwipeItemWidth, _contentView.Frame.Height);
+
+ if (swipeItem is SwipeItemView horizontalSwipeItemView)
+ {
+ var swipeItemViewSizeRequest = horizontalSwipeItemView.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+ return new Size(swipeItemViewSizeRequest.Request.Width > 0 ? (float)swipeItemViewSizeRequest.Request.Width : SwipeItemWidth, _contentView.Frame.Height);
+ }
+ }
+ else
+ {
+ if (swipeItem is SwipeItem)
+ return new Size(_contentView.Frame.Width / items.Count, GetSwipeItemHeight());
+
+ if (swipeItem is SwipeItemView horizontalSwipeItemView)
+ {
+ var swipeItemViewSizeRequest = horizontalSwipeItemView.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+ return new Size(_contentView.Frame.Width / items.Count, swipeItemViewSizeRequest.Request.Height > 0 ? (float)swipeItemViewSizeRequest.Request.Height : _contentView.Frame.Height);
+ }
+ }
+
+ return Size.Zero;
+ }
+
+ double GetSwipeItemHeight()
+ {
+ var items = GetSwipeItemsByDirection();
+
+ if (items.Any(s => s is SwipeItemView))
+ {
+ var itemsHeight = new List<double>();
+
+ foreach (var swipeItem in items)
+ {
+ if (swipeItem is SwipeItemView swipeItemView)
+ {
+ var swipeItemViewSizeRequest = swipeItemView.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+ itemsHeight.Add(swipeItemViewSizeRequest.Request.Height);
+ }
+ }
+
+ return itemsHeight.Max();
+ }
+
+ return _contentView.Frame.Height;
+ }
+
+ bool ValidateSwipeDirection()
+ {
+ var swipeItems = GetSwipeItemsByDirection();
+ return IsValidSwipeItems(swipeItems);
+ }
+
+ double GetSwipeOffset(CGPoint initialPoint, CGPoint endPoint)
+ {
+ double swipeOffset = 0;
+
+ switch (_swipeDirection)
+ {
+ case SwipeDirection.Left:
+ case SwipeDirection.Right:
+ swipeOffset = endPoint.X - initialPoint.X;
+ break;
+ case SwipeDirection.Up:
+ case SwipeDirection.Down:
+ swipeOffset = endPoint.Y - initialPoint.Y;
+ break;
+ }
+
+ return swipeOffset;
+ }
+
+ void ProcessTouchSwipeItems(CGPoint point)
+ {
+ var swipeItems = GetSwipeItemsByDirection();
+
+ if (swipeItems == null || _swipeItemsRect == null)
+ return;
+
+ int i = 0;
+
+ foreach (var swipeItemRect in _swipeItemsRect)
+ {
+ var swipeItem = swipeItems[i];
+
+ var swipeItemX = swipeItemRect.Left;
+ var swipeItemY = swipeItemRect.Top;
+
+ if (TouchInsideContent(swipeItemX, swipeItemY, swipeItemRect.Width, swipeItemRect.Height, point.X, point.Y))
+ {
+ ExecuteSwipeItem(swipeItem);
+
+ if (swipeItems.SwipeBehaviorOnInvoked != SwipeBehaviorOnInvoked.RemainOpen)
+ ResetSwipe();
+
+ break;
+ }
+
+ i++;
+ }
+ }
+
+ UIViewController GetViewController()
+ {
+ var window = UIApplication.SharedApplication.KeyWindow;
+ var viewController = window.RootViewController;
+
+ while (viewController.PresentedViewController != null)
+ viewController = viewController.PresentedViewController;
+
+ return viewController;
+ }
+
+ UINavigationController GetUINavigationController(UIViewController controller)
+ {
+ if (controller != null)
+ {
+ if (controller is UINavigationController)
+ {
+ return (controller as UINavigationController);
+ }
+
+ if (controller.ChildViewControllers.Any())
+ {
+ var childs = controller.ChildViewControllers.Count();
+
+ for (int i = 0; i < childs; i++)
+ {
+ var child = GetUINavigationController(controller.ChildViewControllers[i]);
+
+ if (child is UINavigationController)
+ {
+ return (child as UINavigationController);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ void ExecuteSwipeItem(ISwipeItem swipeItem)
+ {
+ if (swipeItem == null)
+ return;
+
+ swipeItem.OnInvoked();
+ }
+
+ void OnParentScrolled(object sender, ScrolledEventArgs e)
+ {
+ ResetSwipe();
+ }
+
+ void OnParentScrolled(object sender, ItemsViewScrolledEventArgs e)
+ {
+ if (e.HorizontalDelta > 10 || e.VerticalDelta > 10)
+ ResetSwipe();
+ }
+
+ void OnCloseRequested(object sender, EventArgs e)
+ {
+ ResetSwipe();
+ }
+
+ void RaiseSwipeStarted()
+ {
+ if (_swipeDirection == null || !ValidateSwipeDirection())
+ return;
+
+ var swipeStartedEventArgs = new SwipeStartedEventArgs(_swipeDirection.Value);
+ ((ISwipeViewController)Element).SendSwipeStarted(swipeStartedEventArgs);
+ }
+
+ void RaiseSwipeChanging()
+ {
+ if (_swipeDirection == null)
+ return;
+
+ var swipeChangingEventArgs = new SwipeChangingEventArgs(_swipeDirection.Value, _swipeOffset);
+ ((ISwipeViewController)Element).SendSwipeChanging(swipeChangingEventArgs);
+ }
+
+ void RaiseSwipeEnded()
+ {
+ if (_swipeDirection == null || !ValidateSwipeDirection())
+ return;
+
+ var swipeEndedEventArgs = new SwipeEndedEventArgs(_swipeDirection.Value);
+ ((ISwipeViewController)Element).SendSwipeEnded(swipeEndedEventArgs);
+ }
+ }
+}
\ No newline at end of file
<Compile Include="CollectionView\CarouselTemplatedCell.cs" />
<Compile Include="Renderers\RefreshViewRenderer.cs" />
<Compile Include="Renderers\ShellNavBarAppearanceTracker.cs" />
+ <Compile Include="Renderers\SwipeViewRenderer.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\StringResources.ar.resx" />
internal class _MasterDetailPageRenderer
{
}
+
+ internal class _SwipeViewRenderer
+ {
+ }
}
\ No newline at end of file