I am trying to implement similar zoom functionality like Instagram mobile app feed screen in xamarin cross-platform app.
Actually I want to add pinch gesture on ImageView
inside ListView
cell, if user pinch on image, it should zoom-in over ListView
. Once user release pinch it should automatically resize to original size and fit to cell.
Help me and provide solution.
Following sample code working but its zooming inside the cell, its not like Instagram zooming overlap/overlay effect.
XAML code
<ListView x:Name="listView" ItemSelected="PlayerList_OnItemSelected" HasUnevenRows="false" RowHeight="300" BackgroundColor="#F6F3F6"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width, Factor=0}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height, Factor=.09}"
RelativeLayout.HeightConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height, Factor=.91}"
RelativeLayout.WidthConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width, Factor=1}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<Frame Margin="5" Padding="5" OutlineColor="White" HasShadow="true">
<RelativeLayout >
<Label Font="Bold,20" Text="{Binding name}" TextColor="Black"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=.0}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=.02}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=.9}"/>
<local:PinchToZoomContainer RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=1}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Factor=.15,
Property=Height}">
<local:PinchToZoomContainer.Content>
<Image x:Name="imgBg" HeightRequest="180" Aspect="AspectFill" BackgroundColor="#F0F3F4" Margin="1,1,1,-1" Source="{Binding thumbnail, Converter={StaticResource GetFeedImage}}" />
</local:PinchToZoomContainer.Content>
</local:PinchToZoomContainer>
<Label FontSize="10" TextColor="#96AAC0" HorizontalTextAlignment="Center" HeightRequest="40" Text="{Binding posted_at}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=.02}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=.81}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=.2}"/>
<Label FontSize="12" TextColor="#5B636E" HorizontalTextAlignment="Start" Text="{Binding description}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=.87}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=.01}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=.98}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=0,
Constant=30}"/>
</RelativeLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Zoom container class
public class PinchToZoomContainer : ContentView
{
double currentScale = 1;
double startScale = 1;
double xOffset = 0;
double yOffset = 0;
public PinchToZoomContainer ()
{
var pinchGesture = new PinchGestureRecognizer ();
pinchGesture.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add (pinchGesture);
}
void OnPinchUpdated (object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started) {
// Store the current scale factor applied to the wrapped user interface element,
// and zero the components for the center point of the translate transform.
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
}
if (e.Status == GestureStatus.Running) {
// Calculate the scale factor to be applied.
currentScale += (e.Scale - 1) * startScale;
currentScale = Math.Max (1, currentScale);
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the X pixel coordinate.
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the Y pixel coordinate.
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
// Calculate the transformed element pixel coordinates.
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
// Apply translation based on the change in origin.
Content.TranslationX = targetX.Clamp (-Content.Width * (currentScale - 1), 0);
Content.TranslationY = targetY.Clamp (-Content.Height * (currentScale - 1), 0);
// Apply scale factor
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed) {
// Store the translation delta's of the wrapped user interface element.
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
}
}
}