Is there a way to use pinch to zoom in shared Xamarin Forms, I only found an implementation for each platform.
You can use the Pan Gesture to implement it.
There is a good sample of wrapping an image in PanContainer available here - Adding a Pan Gesture Recognizer.
The pan gesture is used for detecting dragging and is implemented with
the PanGestureRecognizer class. A common scenario for the pan gesture
is to horizontally and vertically drag an image, so that all of the
image content can be viewed when it's being displayed in a viewport
smaller than the image dimensions. This is accomplished by moving the
image within the viewport.
Simple Example :
<Image Source="MonoMonkey.jpg">
<Image.GestureRecognizers>
<PanGestureRecognizer PanUpdated="OnPanUpdated" />
</Image.GestureRecognizers>
</Image>
Pan Container example XAML:
<AbsoluteLayout>
<local:PanContainer>
<Image Source="MonoMonkey.jpg" WidthRequest="1024" HeightRequest="768" />
</local:PanContainer>
</AbsoluteLayout>
Code behind :
public class PanContainer : ContentView
{
double x, y;
public PanContainer ()
{
// Set PanGestureRecognizer.TouchPoints to control the
// number of touch points needed to pan
var panGesture = new PanGestureRecognizer ();
panGesture.PanUpdated += OnPanUpdated;
GestureRecognizers.Add (panGesture);
}
void OnPanUpdated (object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType) {
case GestureStatus.Running:
// Translate and ensure we don't pan beyond the wrapped user interface element bounds.
Content.TranslationX =
Math.Max (Math.Min (0, x + e.TotalX), -Math.Abs (Content.Width - App.ScreenWidth));
Content.TranslationY =
Math.Max (Math.Min (0, y + e.TotalY), -Math.Abs (Content.Height - App.ScreenHeight));
break;
case GestureStatus.Completed:
// Store the translation applied during the pan
x = Content.TranslationX;
y = Content.TranslationY;
break;
}
}
}
You can try this modification I made and verified on .NET Maui to TBertuzzi/Xamarin.Forms.PinchZoomImage to:
Adjust image boundaries for not offsetting it while pan gesture.
Sleep for .2 seconds after zoom for pan prevent.
Do not zoom over 4 times.
And it can be used as container like this after importing from appropriate namespace:
<? xml version = "1.0" encoding = "utf-8" ?>
< ContentPage xmlns = "http://schemas.microsoft.com/dotnet/2021/maui"
xmlns: x = "http://schemas.microsoft.com/winfx/2009/xaml"
xmlns: collects = "clr-namespace:Mab.Collects"
x: Class = "N3ema.Secondpageimageshow" >
< ContentPage.Content >
< collects:Zoom >
< collects:Zoom.Content >
< StackLayout >
< Image Source = "shahid.jpeg"
HorizontalOptions = "Fill"
Aspect = "Fill"
VerticalOptions = "FillAndExpand" />
</ StackLayout >
</ collects:Zoom.Content >
</ collects:Zoom >
</ ContentPage.Content >
</ ContentPage >
Related
I'm developing a Xamarin Forms iOS app. In the xaml file, there is a grid.
<Grid x:Name="QrCodeSite" HeightRequest="300" Margin="37, 37, 37, 0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
</Grid>
In the related .cs file, I use ZXing.Net.Mobile.Forms to generate a QR code and place it in the grid. And I put my logo in the same grid, which will finally appear at the center of the QR code.
var barcode = new ZXingBarcodeImageView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
barcode.BarcodeFormat = ZXing.BarcodeFormat.QR_CODE;
barcode.BarcodeOptions.Width = 650;
barcode.BarcodeOptions.Height = 650;
barcode.BarcodeOptions.Margin = 1;
barcode.BarcodeValue = value;
var img = new Image
{
Source = "logo.png",
WidthRequest = 70,
HeightRequest = 70,
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center
};
QrCodeSite.Children.Clear();
QrCodeSite.Children.Add(barcode);
QrCodeSite.Children.Add(img);
The problem is, maybe my phone (iPhone 6s plus) is too slow, sometimes the logo appears first and after a lag (around 1 second) the QR code is shown. How can I merge the QR code and the logo into one image and then add it to the grid?
You can using SkiaSharp to display Image or merge images . Having a look at how to Display SkiaSharp bitmaps to download sample project to research at it.
Based on Drawing on existing bitmaps reference , you can modify it as follow :
public partial class MonkeyMoustachePage : ContentPage
{
SKBitmap monkeyBitmap;
public MonkeyMoustachePage()
{
Title = "Monkey Moustache";
monkeyBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
"SkiaSharpFormsDemos.Media.MonkeyFace.png");
SKBitmap iconImage = BitmapExtensions.LoadBitmapResource(GetType(),
"SkiaSharpFormsDemos.Media.GooglePlaylogo.png");
int offset = monkeyBitmap.Width / 2 - iconImage.Width / 2;
int offsetTop = monkeyBitmap.Height / 2 - iconImage.Height / 2;
// Create canvas based on bitmap
using (SKCanvas canvas = new SKCanvas(monkeyBitmap))
{
canvas.DrawBitmap(iconImage, SKRect.Create(offset, offsetTop, iconImage.Width, iconImage.Height));
}
// Create SKCanvasView to view result
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
//save the new image
using (MemoryStream memStream = new MemoryStream())
using (SKManagedWStream wstream = new SKManagedWStream(memStream))
{
monkeyBitmap.Encode(wstream, imageFormat, quality);
byte[] data = memStream.ToArray();
// Check the data array for content!
bool success = await DependencyService.Get<IPhotoLibrary>().SavePhotoAsync(data, folder, filename);
// Check return value for success!
}
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
canvas.DrawBitmap(monkeyBitmap, info.Rect, BitmapStretch.Uniform);
}
}
Then you will see a logo icon will display in original image :
If you want to save SkiaSharp bitmaps to files , have a look at this :https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/bitmaps/saving#exploring-the-image-formats
Note: BitmapExtensions.cs file is from sample project .By the way , when adding image to project , you need to set Build ACtion of image be Embedded resource .As follow :
I'm using FFImageLoading to display svg icons in my Xamarin.Forms project. My understanding is that the Height and Width requests must be set explicitly in order for the SVG to be rendered properly. I'm getting a lot of pixilation whenever I try to bind the Height/Width requests to values on the ViewModel (I have this as a need because the desired size depends on data). If I set the size explicitly everything looks fine.
Does SvgCachedImage not redraw the SVG whenever the bindings for Height/Width request change?
If not, is there a way for me to explicitly force them to invalidate and redraw when the size changes?
The blur issue was resolved by setting Horizontal and Vertical options to fill instead of Center:
<Grid>
<ffimageloadingsvg:SvgCachedImage BackgroundColor="Transparent"
Margin="{Binding HarmonicIconMargin}"
HorizontalOptions="Fill"
VerticalOptions="Fill"
WidthRequest="{Binding HarmonicIconWidth}"
HeightRequest="{Binding HarmonicIconWidth}"
Source="{Binding CurrentTestItem, Converter={StaticResource TestItemToHarmonicIconConverter}}" />
</Grid>
At that point it seemed be ignoring the height/width requests. I could've experimented around with that more (perhaps the request was for too much space) but I found that binding the margin to a computed property effectively enabled me to control the size of the SVG Image while not causing it to become blurred.
In order to solve svg blur issue when scale size of view,
Change SvgImageSource.VectorWidth or SvgImageSource.VectorHeight
Reload Image
protected override void OnSizeAllocated(double width, double height)
{
if (0 < width && 0 < height && && Source is SvgImageSource imageSource)
{
imageSource.VectorWidth = (int)Math.Ceiling(width);
imageSource.VectorHeight = (int)Math.Ceiling(height);
svgImage.ReloadImage();
base.OnSizeAllocated(width, height);
}
}
According to FFImageLoading source code, SVG image size is determined by SvgImageSource.VectorWidth or SvgImageSource.VectorHeight.
double sizeX = VectorWidth;
double sizeY = VectorHeight;
if (UseDipUnits)
{
sizeX = VectorWidth.DpToPixels();
sizeY = VectorHeight.DpToPixels();
}
if (sizeX <= 0 && sizeY <= 0)
{
if (picture.CullRect.Width > 0)
sizeX = picture.CullRect.Width;
else
sizeX = 400;
if (picture.CullRect.Height > 0)
sizeY = picture.CullRect.Height;
else
sizeY = 400;
}
else if (sizeX > 0 && sizeY <= 0)
{
sizeY = (int)(sizeX / picture.CullRect.Width * picture.CullRect.Height);
}
else if (sizeX <= 0 && sizeY > 0)
{
sizeX = (int)(sizeY / picture.CullRect.Height * picture.CullRect.Width);
}
resolvedData.ImageInformation.SetType(ImageInformation.ImageType.SVG);
using (var bitmap = new SKBitmap(new SKImageInfo((int)sizeX, (int)sizeY)))
using (var canvas = new SKCanvas(bitmap))
using (var paint = new SKPaint())
{
canvas.Clear(SKColors.Transparent);
var scaleX = (float)sizeX / picture.CullRect.Width;
var scaleY = (float)sizeY / picture.CullRect.Height;
var matrix = SKMatrix.MakeScale(scaleX, scaleY);
canvas.DrawPicture(picture, ref matrix, paint);
canvas.Flush();
token.ThrowIfCancellationRequested();
return await Decode(picture, bitmap, resolvedData).ConfigureAwait(false);
}
binding cause a pixellation because view's initial width and height are used as VectorWidth and VectorHeight, which is -1 or somthing you set as default for binding property. Therefore, your svg image resolution set too low at first and then binding process scales up view without redrawing svg image.
I have a UWP application where I am rotating an image to a 90 degree angle. I have this image and a Canvas inside a Grid because I want Canvas to be on top of the image so from the code I can create some thumb controls and do a drag an drop.
If I don't apply the rotate transform the image is aligned properly inside the Grid like shown below.
On the other hand if I specify a Rotate Transform, the image rotates but it never scales to the height and width of the container as shown below.
I saw this post here Rotating and scaling image but I don't know how to get it to work in UWP. Please help. Here is my xaml. Ultimately what I want is, after the image has been rotated it should fit to the dimensions of the Grid scaling the height and width.
Edit: Please see the solution here, that is exactly what I want in UWP. fit image height and width after rotating WPF
<Grid x:Name="gridBarImagePanel" Grid.Row="4" BorderBrush="Black" BorderThickness="2"
Height="476" Width="625">
<Image x:Name="BarCodeImage" Source="..\SampleImage\demo.png"
RenderTransformOrigin="0.54,0.40" Height="476" Width="625">
<Image.RenderTransform>
<RotateTransform Angle="90"></RotateTransform>
</Image.RenderTransform>
</Image>
<Canvas x:Name="cnvBarCodeImage" Canvas.ZIndex="100" VerticalAlignment="Stretch">
</Canvas>
</Grid>
Image when rotated does not respect the height and width of the parent container
RotateTransform is used to rotate transform uwp XAML. A RotateTransform is defined by an Angle that rotates an object through an arc around the point enterX, CenterY. But a transform is typically used to fill the UIElement.RenderTransform property and keep aspect ratio. For your scenario, we need use BitmapDecoder to re-render the image and keep aspect ratio. Please refer the following code.
private async void AppBarButton_Click(object sender, RoutedEventArgs e)
{
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/test.jpg"));
using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.Read),
memStream = new InMemoryRandomAccessStream())
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
uint originalWidth = decoder.PixelWidth;
uint originalHeight = decoder.PixelHeight;
BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(memStream, decoder);
encoder.BitmapTransform.ScaledWidth = (uint)(originalHeight * 1.0);
encoder.BitmapTransform.ScaledHeight = (uint)(originalWidth * 1.0);
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Linear;
//Rotate 180
encoder.BitmapTransform.Rotation = BitmapRotation.Clockwise90Degrees;
await encoder.FlushAsync();
memStream.Seek(0);
fileStream.Seek(0);
fileStream.Size = 0;
var bitmap = new BitmapImage();
bitmap.SetSource(memStream);
MyImg.Source = bitmap;
}
}
Please note: if we rotate image to 90 ° and keep aspect ratio, the image will be distortion.
any ideas how to prepare such an element as at the picture ?
I need 5 buttons to be there so i could swipe through them but only 3 one them are visible all the time. I need this to work on android and ios in xamarin forms.
Try this Add ScrollView and set it's orientation to horizontal
ScrollView = new ScrollView
{
Orientation = ScrollOrientation.Horizontal
};
//ScrollView.Scrolled += ScrollView_Scrolled;
ItemsStackLayout = new StackLayout
{
Orientation = StackOrientation.Horizontal,
Padding = new Thickness(0),
Spacing = 0,
HorizontalOptions = LayoutOptions.FillAndExpand
};
ScrollView.Content = ItemsStackLayout;
you can set the width of the elements inside 1/3 of the view width
FYI I use the same in my app
I have a page whose contents are horizontally wider than the phone, sort of like a Panorama but without all the parallax stuff, and I want to let the user flick to scroll, and also press a button to scroll to specified offsets within the scrollviewer. ScrollToHorizontalOffset works fine for the button, except that it loses the animation, and I want to retain the animation.
I am doing a very similar thing. I have created WizardControl and WizardPage user controls. The user can flick between pages or use next and previous buttons.
I have created a slide effect animation to move between pages. Here is the code:
Storyboard sb = SlideEffect(MainPanel, (-_pageWidth * pagesToMove));
sb.Completed += Slide_Completed;
sb.Begin();
private Storyboard SlideEffect(UIElement controlToAnimate, double positionToMove)
{
//Get position of stackpanel
GeneralTransform gt = controlToAnimate.TransformToVisual(MainGrid);
Point p = gt.Transform(new Point(0, 0));
//add new storyboard and animation
Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation { To = p.X + positionToMove };
Storyboard.SetTarget(da, controlToAnimate);
Storyboard.SetTargetProperty(da, new PropertyPath("(controlToAnimate.RenderTransform).(TransformTranslate.X)"));
ExponentialEase ee = new ExponentialEase { Exponent = 6.0, EasingMode = EasingMode.EaseOut };
da.EasingFunction = ee;
sb.Children.Add(da);
return sb;
}
MainPanel is a horizontal stack panel. Wizard pages are added to it.
_pageWidth matters as this works in both portrait and landscape mode.
pagestoMove is used as I have a button to return directly to the first page.
UPDATE - added Wizard main panel def and add page method
<StackPanel
x:Name="MainPanel"
Grid.Row="1"
Orientation="Horizontal"
RenderTransformOrigin="0.5,0.5"
Margin="0,0,0,10"
Width="480">
<StackPanel.RenderTransform>
<TranslateTransform
X="0" />
</StackPanel.RenderTransform>
</StackPanel>
public void AddPage(WizardPageControl page)
{
MainPanel.Width += _pageWidth;
page.PageWidth = _pageWidth;
double newRight = (MainPanel.Width - _pageWidth) * -1;
MainPanel.Margin = new Thickness(0, 0, newRight, 10);
MainPanel.Children.Add(page);
_wizardPageList.Add(page);
TotalPages++;
}