SwipeView (#7603)
authorJavier Suárez Ruiz <javiersuarezruiz@hotmail.com>
Sat, 23 Nov 2019 00:47:01 +0000 (01:47 +0100)
committerShane Neuville <shneuvil@microsoft.com>
Sat, 23 Nov 2019 00:47:01 +0000 (17:47 -0700)
* Implemented SwipeView Spec

* Fixed close SwipeView when scrolling ScrollView in iOS

* Removed unnecessary SwipeItemInvokedEventArgs

* Separate ISwipeItem in a new file

* Changed BaseSwipeEventArgs to be abstract

* Changed SwipeView SwipeItems properties to use OneWay BindingMode

* SwipeDirectionHelpers is now no EditorBrowsable

* Removed unnecessary ContextItem

* Changed SwipeItems base type from BO to Element

* Added defaultValueCreator for SwipeItems (and new sample)

* Changes in UWP SwipeViewRenderer to change some SwipeItems validations

* Renamed CustomSwipeItem to SwipeItemView
Added Close method
Added new SwipeView sample in Core Gallery
Propagate BindingContext from SwipeView to SwipeItems
Fixed bug in Android where occasionally was not rendering the SwipeItem (BindableLayout)

* Moved command execution from the renderers to the abstraction

* Implemented Close method on UWP
Fixed issue to notify SwipeItems changes (add/remove) on UWP
Implemented Uri IconSource (fix SwipeItem using an Url on UWP)

* Updated CustomSwipeItem samples

* Fixed issue on Android (not rendering SwipeItem occasionally)

* Avoid use MessagingCenter to close the SwipeView when scrolling

* Added new Core Gallery sample using custom size SwipeItems
Fixed icon position on Android with big SwipeItems
Fixed custom SwipeItem size issue

* Fixed vertical swipe threshold calc

* Created ISwipeViewController
Moved SwipeItemView to a new class
Moved SwipeEventArgs to a new class
Changes in touch events in Android to have the same behavior as in Android

* Unsubscribe CollectionChanged from the OldElement on UWP renderer

* Allow open the swipeView if the width of the SwipeItems exceeds the width of the SwipeView
Fixed Android SwipeItem icon position calc issue

* Updated SwipeItems to use ISwipeViewController

* Fixed typo

* Fixed typo

* - remove EditorBrowsable

* Changed SwipeTransitionMode enum values

* - fix samsung fussiness

* - fix compile error for UAP

63 files changed:
Stubs/Xamarin.Forms.Platform.cs
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue1704.cs
Xamarin.Forms.Controls/CoreGallery.cs
Xamarin.Forms.Controls/CoreGalleryPages/SwipeViewCoreGalleryPage.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/AddRemoveSwipeItemsGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/BasicSwipeGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/BasicSwipeGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CloseSwipeGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSizeSwipeViewGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSizeSwipeViewGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSwipeItemGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSwipeItemViewGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSwipeItemViewGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomizeSwipeItemGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/ResourceSwipeItemsGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/ResourceSwipeItemsGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeBehaviorOnInvokedGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeBindableLayoutGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeBindableLayoutGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeCarouselViewGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeCarouselViewGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeCollectionViewGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeHorizontalCollectionViewGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeHorizontalCollectionViewGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeItemIconGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeListViewGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeListViewGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeTransitionModeGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeVerticalCollectionViewGallery.xaml [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeVerticalCollectionViewGallery.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeViewEventsGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeViewGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj
Xamarin.Forms.Core/ISwipeItem.cs [new file with mode: 0644]
Xamarin.Forms.Core/ISwipeViewController.cs [new file with mode: 0644]
Xamarin.Forms.Core/PlatformConfiguration/AndroidSpecific/SwipeView.cs [new file with mode: 0644]
Xamarin.Forms.Core/PlatformConfiguration/iOSSpecific/SwipeView.cs [new file with mode: 0644]
Xamarin.Forms.Core/SwipeBehaviorOnInvoked.cs [new file with mode: 0644]
Xamarin.Forms.Core/SwipeDirection.cs
Xamarin.Forms.Core/SwipeEventArgs.cs [new file with mode: 0644]
Xamarin.Forms.Core/SwipeItem.cs [new file with mode: 0644]
Xamarin.Forms.Core/SwipeItemView.cs [new file with mode: 0644]
Xamarin.Forms.Core/SwipeItems.cs [new file with mode: 0644]
Xamarin.Forms.Core/SwipeMode.cs [new file with mode: 0644]
Xamarin.Forms.Core/SwipeView.cs [new file with mode: 0644]
Xamarin.Forms.CustomAttributes/TestAttributes.cs
Xamarin.Forms.Platform.Android/Properties/AssemblyInfo.cs
Xamarin.Forms.Platform.Android/Renderers/SwipeViewRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj
Xamarin.Forms.Platform.UAP/Extensions/ImageExtensions.cs [changed mode: 0644->0755]
Xamarin.Forms.Platform.UAP/FileImageSourceHandler.cs [changed mode: 0644->0755]
Xamarin.Forms.Platform.UAP/FontImageSourceHandler.cs [changed mode: 0644->0755]
Xamarin.Forms.Platform.UAP/IIconElementHandler.cs [changed mode: 0644->0755]
Xamarin.Forms.Platform.UAP/Properties/AssemblyInfo.cs [changed mode: 0644->0755]
Xamarin.Forms.Platform.UAP/SwipeViewRenderer.cs [new file with mode: 0755]
Xamarin.Forms.Platform.UAP/UriImageSourceHandler.cs
Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj
Xamarin.Forms.Platform.iOS/Properties/AssemblyInfo.cs
Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/SwipeViewRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj
Xamarin.Forms.Platform/Xamarin.Forms.Platform.cs

index b3628fe..1a88b09 100644 (file)
@@ -165,6 +165,10 @@ namespace Xamarin.Forms.Platform
        [RenderWith(typeof(RefreshViewRenderer))]
 #endif
        internal class _RefreshViewRenderer { }
+#if !TIZEN4_0
+       [RenderWith(typeof(SwipeViewRenderer))]
+#endif
+       internal class _SwipeViewRenderer { }
 }
 
 
index 86594a4..1160456 100644 (file)
@@ -459,7 +459,7 @@ namespace Xamarin.Forms.Controls.Issues
                                _abortStressTest = false;
 
                                int.TryParse(_stressTestItertionEntry.Text, out _stressTestIterationCount);
-
+        
 #if __UWP__
                                Windows.System.Threading.ThreadPool.RunAsync(delegate { runStressTest(); });
 #else
index 8d9277a..011bbac 100644 (file)
@@ -13,6 +13,7 @@ using Xamarin.Forms.Controls.GalleryPages.VisualStateManagerGalleries;
 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
 {
@@ -350,6 +351,8 @@ 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"),
diff --git a/Xamarin.Forms.Controls/CoreGalleryPages/SwipeViewCoreGalleryPage.cs b/Xamarin.Forms.Controls/CoreGalleryPages/SwipeViewCoreGalleryPage.cs
new file mode 100644 (file)
index 0000000..446b30f
--- /dev/null
@@ -0,0 +1,118 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/AddRemoveSwipeItemsGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/AddRemoveSwipeItemsGallery.cs
new file mode 100644 (file)
index 0000000..df5ebf3
--- /dev/null
@@ -0,0 +1,118 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/BasicSwipeGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/BasicSwipeGallery.xaml
new file mode 100644 (file)
index 0000000..d45b49a
--- /dev/null
@@ -0,0 +1,131 @@
+<?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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/BasicSwipeGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/BasicSwipeGallery.xaml.cs
new file mode 100644 (file)
index 0000000..17d5c73
--- /dev/null
@@ -0,0 +1,19 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CloseSwipeGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CloseSwipeGallery.cs
new file mode 100644 (file)
index 0000000..a0f4f75
--- /dev/null
@@ -0,0 +1,66 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSizeSwipeViewGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSizeSwipeViewGallery.xaml
new file mode 100644 (file)
index 0000000..3ff9a1e
--- /dev/null
@@ -0,0 +1,64 @@
+<?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>
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSizeSwipeViewGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSizeSwipeViewGallery.xaml.cs
new file mode 100644 (file)
index 0000000..306f10e
--- /dev/null
@@ -0,0 +1,13 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSwipeItemGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSwipeItemGallery.cs
new file mode 100644 (file)
index 0000000..c64759c
--- /dev/null
@@ -0,0 +1,28 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSwipeItemViewGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSwipeItemViewGallery.xaml
new file mode 100644 (file)
index 0000000..3523bcd
--- /dev/null
@@ -0,0 +1,99 @@
+<?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>
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSwipeItemViewGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomSwipeItemViewGallery.xaml.cs
new file mode 100644 (file)
index 0000000..cc7ea0d
--- /dev/null
@@ -0,0 +1,17 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomizeSwipeItemGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/CustomizeSwipeItemGallery.cs
new file mode 100644 (file)
index 0000000..ec132ab
--- /dev/null
@@ -0,0 +1,123 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/ResourceSwipeItemsGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/ResourceSwipeItemsGallery.xaml
new file mode 100644 (file)
index 0000000..d008353
--- /dev/null
@@ -0,0 +1,101 @@
+<?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>
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/ResourceSwipeItemsGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/ResourceSwipeItemsGallery.xaml.cs
new file mode 100644 (file)
index 0000000..9ed6068
--- /dev/null
@@ -0,0 +1,17 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeBehaviorOnInvokedGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeBehaviorOnInvokedGallery.cs
new file mode 100644 (file)
index 0000000..21a9e1c
--- /dev/null
@@ -0,0 +1,81 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeBindableLayoutGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeBindableLayoutGallery.xaml
new file mode 100644 (file)
index 0000000..427944d
--- /dev/null
@@ -0,0 +1,101 @@
+<?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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeBindableLayoutGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeBindableLayoutGallery.xaml.cs
new file mode 100644 (file)
index 0000000..924f1fb
--- /dev/null
@@ -0,0 +1,72 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeCarouselViewGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeCarouselViewGallery.xaml
new file mode 100644 (file)
index 0000000..2e3c5ff
--- /dev/null
@@ -0,0 +1,109 @@
+<?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>
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeCarouselViewGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeCarouselViewGallery.xaml.cs
new file mode 100644 (file)
index 0000000..7c23b6e
--- /dev/null
@@ -0,0 +1,17 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeCollectionViewGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeCollectionViewGallery.cs
new file mode 100644 (file)
index 0000000..46a9bae
--- /dev/null
@@ -0,0 +1,21 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeHorizontalCollectionViewGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeHorizontalCollectionViewGallery.xaml
new file mode 100644 (file)
index 0000000..bb5cff4
--- /dev/null
@@ -0,0 +1,108 @@
+<?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>
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeHorizontalCollectionViewGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeHorizontalCollectionViewGallery.xaml.cs
new file mode 100644 (file)
index 0000000..5400c98
--- /dev/null
@@ -0,0 +1,17 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeItemIconGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeItemIconGallery.cs
new file mode 100644 (file)
index 0000000..7702ecb
--- /dev/null
@@ -0,0 +1,153 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeListViewGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeListViewGallery.xaml
new file mode 100644 (file)
index 0000000..a4423dd
--- /dev/null
@@ -0,0 +1,105 @@
+<?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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeListViewGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeListViewGallery.xaml.cs
new file mode 100644 (file)
index 0000000..2f639a1
--- /dev/null
@@ -0,0 +1,17 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeTransitionModeGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeTransitionModeGallery.cs
new file mode 100644 (file)
index 0000000..af0609d
--- /dev/null
@@ -0,0 +1,199 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeVerticalCollectionViewGallery.xaml b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeVerticalCollectionViewGallery.xaml
new file mode 100644 (file)
index 0000000..f35764a
--- /dev/null
@@ -0,0 +1,100 @@
+<?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>
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeVerticalCollectionViewGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeVerticalCollectionViewGallery.xaml.cs
new file mode 100644 (file)
index 0000000..7236086
--- /dev/null
@@ -0,0 +1,17 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeViewEventsGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeViewEventsGallery.cs
new file mode 100644 (file)
index 0000000..879c7e6
--- /dev/null
@@ -0,0 +1,86 @@
+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
diff --git a/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeViewGallery.cs b/Xamarin.Forms.Controls/GalleryPages/SwipeViewGalleries/SwipeViewGallery.cs
new file mode 100644 (file)
index 0000000..ad7ce85
--- /dev/null
@@ -0,0 +1,53 @@
+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
index 3d38858..29aa6ec 100644 (file)
   </ItemGroup>
   <ItemGroup>
     <Folder Include="GalleryPages\CollectionViewGalleries\CarouselViewGalleries\" />
+    <Folder Include="GalleryPages\SwipeViewGalleries\" />
   </ItemGroup>
   
   <Target Name="CreateControllGalleryConfig" BeforeTargets="Build">
diff --git a/Xamarin.Forms.Core/ISwipeItem.cs b/Xamarin.Forms.Core/ISwipeItem.cs
new file mode 100644 (file)
index 0000000..34a97f3
--- /dev/null
@@ -0,0 +1,14 @@
+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
diff --git a/Xamarin.Forms.Core/ISwipeViewController.cs b/Xamarin.Forms.Core/ISwipeViewController.cs
new file mode 100644 (file)
index 0000000..b8f2b6a
--- /dev/null
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+       public interface ISwipeViewController
+       {
+               void SendSwipeStarted(SwipeStartedEventArgs args);
+               void SendSwipeChanging(SwipeChangingEventArgs args);
+               void SendSwipeEnded(SwipeEndedEventArgs args);
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Core/PlatformConfiguration/AndroidSpecific/SwipeView.cs b/Xamarin.Forms.Core/PlatformConfiguration/AndroidSpecific/SwipeView.cs
new file mode 100644 (file)
index 0000000..93d7e91
--- /dev/null
@@ -0,0 +1,36 @@
+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
diff --git a/Xamarin.Forms.Core/PlatformConfiguration/iOSSpecific/SwipeView.cs b/Xamarin.Forms.Core/PlatformConfiguration/iOSSpecific/SwipeView.cs
new file mode 100644 (file)
index 0000000..8c85dd1
--- /dev/null
@@ -0,0 +1,36 @@
+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
diff --git a/Xamarin.Forms.Core/SwipeBehaviorOnInvoked.cs b/Xamarin.Forms.Core/SwipeBehaviorOnInvoked.cs
new file mode 100644 (file)
index 0000000..0afdd6e
--- /dev/null
@@ -0,0 +1,9 @@
+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
index 28ef0aa..8864c70 100644 (file)
@@ -1,4 +1,6 @@
 using System;
+using System.ComponentModel;
+
 namespace Xamarin.Forms
 {
        [Flags]
@@ -9,4 +11,42 @@ namespace Xamarin.Forms
                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
diff --git a/Xamarin.Forms.Core/SwipeEventArgs.cs b/Xamarin.Forms.Core/SwipeEventArgs.cs
new file mode 100644 (file)
index 0000000..7d7451e
--- /dev/null
@@ -0,0 +1,40 @@
+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
diff --git a/Xamarin.Forms.Core/SwipeItem.cs b/Xamarin.Forms.Core/SwipeItem.cs
new file mode 100644 (file)
index 0000000..c197dec
--- /dev/null
@@ -0,0 +1,27 @@
+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
diff --git a/Xamarin.Forms.Core/SwipeItemView.cs b/Xamarin.Forms.Core/SwipeItemView.cs
new file mode 100644 (file)
index 0000000..e78b2aa
--- /dev/null
@@ -0,0 +1,36 @@
+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
diff --git a/Xamarin.Forms.Core/SwipeItems.cs b/Xamarin.Forms.Core/SwipeItems.cs
new file mode 100644 (file)
index 0000000..49739cc
--- /dev/null
@@ -0,0 +1,120 @@
+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
diff --git a/Xamarin.Forms.Core/SwipeMode.cs b/Xamarin.Forms.Core/SwipeMode.cs
new file mode 100644 (file)
index 0000000..f1323d9
--- /dev/null
@@ -0,0 +1,8 @@
+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
diff --git a/Xamarin.Forms.Core/SwipeView.cs b/Xamarin.Forms.Core/SwipeView.cs
new file mode 100644 (file)
index 0000000..561371a
--- /dev/null
@@ -0,0 +1,94 @@
+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
index 08c6211..002cddc 100644 (file)
@@ -201,7 +201,8 @@ namespace Xamarin.Forms.CustomAttributes
                        EntryCell,
                        Editor,
                        DatePicker,
-                       CheckBox
+                       CheckBox,
+                       SwipeView
                }
 
                public enum Layouts
@@ -744,6 +745,13 @@ namespace Xamarin.Forms.CustomAttributes
                        ThumbColor
                }
 
+               public enum SwipeView
+               {
+                       RightItems,
+                       TopItems,
+                       BottomItems
+               }
+
                public enum CheckBox
                {
                        IsChecked,
index 3a5f06c..d19a592 100644 (file)
@@ -21,6 +21,7 @@ using Xamarin.Forms.Platform.Android;
 [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))]
diff --git a/Xamarin.Forms.Platform.Android/Renderers/SwipeViewRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/SwipeViewRenderer.cs
new file mode 100644 (file)
index 0000000..72187a2
--- /dev/null
@@ -0,0 +1,1083 @@
+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
index c39634a..63141e8 100644 (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.
old mode 100644 (file)
new mode 100755 (executable)
index 824d707..04c4cce
@@ -42,6 +42,32 @@ namespace Xamarin.Forms.Platform.UWP
                        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();
old mode 100644 (file)
new mode 100755 (executable)
index cc2c9d2..cad7ad4
@@ -21,6 +21,19 @@ namespace Xamarin.Forms.Platform.UWP
                        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;
old mode 100644 (file)
new mode 100755 (executable)
index c44977f..c04ce8f
@@ -51,6 +51,24 @@ namespace Xamarin.Forms.Platform.UWP
                        }
                }
 
+               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;
old mode 100644 (file)
new mode 100755 (executable)
index b7117bb..5d7db0d
@@ -6,6 +6,7 @@ namespace Xamarin.Forms.Platform.UWP
 {
        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
old mode 100644 (file)
new mode 100755 (executable)
index 132c994..788409a
@@ -18,6 +18,7 @@ using Xamarin.Forms.Platform.UWP;
 [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))]
diff --git a/Xamarin.Forms.Platform.UAP/SwipeViewRenderer.cs b/Xamarin.Forms.Platform.UAP/SwipeViewRenderer.cs
new file mode 100755 (executable)
index 0000000..81a2247
--- /dev/null
@@ -0,0 +1,492 @@
+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
index f41c557..ccd7cc0 100644 (file)
@@ -3,20 +3,47 @@ using System.IO;
 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;
index 1f70769..f64128b 100644 (file)
       <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" />
index e375d5e..c915edc 100644 (file)
@@ -17,6 +17,7 @@ using UIKit;
 [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))]
index f350924..f027439 100644 (file)
@@ -1222,6 +1222,7 @@ namespace Xamarin.Forms.Platform.iOS
                        {
                                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,
index 1434bc3..1adde8c 100644 (file)
@@ -391,7 +391,11 @@ namespace Xamarin.Forms.Platform.iOS
                                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 =>
                        {
@@ -441,11 +445,23 @@ namespace Xamarin.Forms.Platform.iOS
                                                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()
diff --git a/Xamarin.Forms.Platform.iOS/Renderers/SwipeViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/SwipeViewRenderer.cs
new file mode 100644 (file)
index 0000000..27eb95a
--- /dev/null
@@ -0,0 +1,1052 @@
+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
index 1b105bf..3c7f83a 100644 (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" />
index 7a7a478..70c4aac 100644 (file)
        internal class _MasterDetailPageRenderer
        {
        }
+
+       internal class _SwipeViewRenderer
+       {
+       }
 }
\ No newline at end of file