WebView automatically zoom in after first change of scale - ios

I have a WebView and i load an image into it. If image is bigger than phone's screen i want to zoom out to have all image on the screen. I do this using WebView.LoadFinished EventHandler. When LoadFinished is triggered i calculate scale to zoom out and set as WebView's MinimumZoomScale and ZoomScale. It works fine but after first zoom in or zoom out image automaticaly zoom in to some specific scale and i can't zoom out. How can i solve it ?
private void InitializeWebView()
{
WebView.ScalesPageToFit = true;
WebView.LoadFinished += WebView_LoadFinished;
}
private void WebView_LoadFinished(object sender, EventArgs e)
{
var contentSize = WebView.ScrollView.ContentSize;
var viewSize = View.Bounds.Size;
var scale = viewSize.Width / contentSize.Width;
WebView.ScrollView.MinimumZoomScale = scale ; //removing this makes that my image doesnt zoom out after loaded
WebView.ScrollView.ZoomScale = scale ;
}
public void LoadAttachment(string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
return;
}
var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var targetUrl = documentsPath + "/" + fileName;
var url = new NSUrl(targetUrl);
if (url != null)
{
var request = new NSUrlRequest(url);
WebView.LoadRequest(request);
//Example request: "/var/mobile/Containers/Data/Application/6BDB8CD2-280B-45A3-9109-8E08E1314739/Documents/26112018110204.jpg"
}
}
I try use Lucas Zhang - MSFT's solution like this:
private void WebView_LoadFinished(object sender, EventArgs e)
{
var contentSize = WebView.ScrollView.ContentSize;
var viewSize = View.Bounds.Size;
var scale = viewSize.Width / contentSize.Width;
WebView.ScrollView.MinimumZoomScale = scale;
WebView.ScrollView.ZoomScale = scale;
NSString str = new NSString($"var meta = document.createElement('meta');meta.content='width=device-width,initial-scale={1.0},minimum-scale={scale},maximum-scale={WebView.ScrollView.MaximumZoomScale}';meta.name='viewport';document.getElementsByTagName('head')[0].appendChild(meta);");
WebView.EvaluateJavascript(str);
}
Screenshots:
Image opened
Image scrolled right
Image after first zoom
It is not visible but on the third screenshot there is still posibility to scroll image right. Not all image is on the screen.

Cause:
Because you didn't refer to the tag meta in your html file.
Solution:
Add the following code in the method WebView_LoadFinished
private void WebView_LoadFinished(object sender, EventArgs e)
{
var contentSize = WebView.ScrollView.ContentSize;
var viewSize = View.Bounds.Size;
var scale = viewSize.Width / contentSize.Width;
WebView.ScrollView.MinimumZoomScale = scale ;
WebView.ScrollView.ZoomScale = scale ;
NSString str = new NSString("var meta = document.createElement('meta');meta.content='width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=2.0';meta.name='viewport';document.getElementsByTagName('head')[0].appendChild(meta);");
webView.EvaluateJavascript(str);
}
minimum-scale=1.0,maximum-scale=2.0
You can set the value of MinimumZoomScale and MaximumZoomScale here.

Related

CATransform3D.MakeScale is moving layer

I am developing a Xamarin.Forms application for iOS. This app consists of an UIView, which has sublayers which are CALayers. They are added like this:
// draw all the pins from the list
foreach (var pin in _control.PinsSource)
{
var point = new CGPoint
{
X = pin.Longitude,
Y = pin.Latitude
};
var shapeLayer = new CAShapeLayer
{
Name = nameof(MapItem),
Path = MakeCircleAtLocation(point, PinRadius).CGPath,
FillColor = UIColor.Red.CGColor
};
Layer.AddSublayer(shapeLayer);
}
// Create a UIBezierPath which is a circle at a certain location of a certain radius.
private static UIBezierPath MakeCircleAtLocation(CGPoint location, nfloat radius)
{
var path = new UIBezierPath();
path.AddArc(location, radius, 0, (float)(Math.PI * 2.0), true);
return path;
}
Then I have a UIPinchGestureRecognizer which can scale the UIView and some other GestureRecognizers like panning.
Scaling and panning the base view works well. The UIView is scaled using a variable called _currentScale. See the full scale method here:
private void HandlePinch(UIPinchGestureRecognizer recognizer)
{
// Prevent the object to become too large or too small
var newScale = (nfloat)Math.Max(MinZoomLevel, Math.Min(_currentScale * recognizer.Scale, MaxZoomLevel));
if (_currentScale != newScale)
{
_currentScale = newScale;
Transform = CGAffineTransform.MakeScale(_currentScale, _currentScale);
foreach (var subLayer in Layer.Sublayers)
{
if (subLayer.Name == nameof(MapItem))
subLayer.Transform = CATransform3D.MakeScale(PinRadius / _currentScale, PinRadius / _currentScale, 1);
}
}
recognizer.Scale = 1;
}
If the sublayer is a map pin, I did like to NOT scale it with the _currentScale, so that's why I am dividing the scale using PinRadius / _currentScale.
The scaling is working fine, however the pin is moving across the map which is weird. See here:
How can I resolve this?
Unfortunately I couldn't find another way then recreating a new CAShapeLayer on every pinch. It's very ugly, but it's working.
private void HandlePinch(UIPinchGestureRecognizer recognizer)
{
// Prevent the object to become too large or too small
var newScale = (nfloat)Math.Max(MinZoomLevel, Math.Min(_currentScale * recognizer.Scale, _maxZoomLevel));
if (_currentScale != newScale)
{
_currentScale = newScale;
_currentPinRadius = _pinRadius / _currentScale;
Transform = CGAffineTransform.MakeScale(_currentScale, _currentScale);
// First layer is a CALayer, so start at 1
for (var i = 1; i < Layer.Sublayers.Length; i++)
{
var caLayer = ((CAShapeLayer)Layer.Sublayers[i]);
var cgPoint = new CGPoint
{
X = _control.PinsSource[i - 1].Longitude,
Y = _control.PinsSource[i - 1].Latitude
};
caLayer.Path = CreateCircle(cgPoint, _currentPinRadius);
}
}
recognizer.Scale = 1;
}

PDF.js displays PDF documents in really low resolution/ blurry almost. Is this how it is?

I am trying to use PDF.js to view PDF documents. I find the display really low resolutions to the point of being blurry. Is there a fix?
// URL of PDF document
var url = "https://www.myFilePath/1Mpublic.pdf";
// Asynchronous download PDF
PDFJS.getDocument(url)
.then(function(pdf) {
return pdf.getPage(1);
})
.then(function(page) {
// Set scale (zoom) level
var scale = 1.2;
// Get viewport (dimensions)
var viewport = page.getViewport(scale);
// Get canvas#the-canvas
var canvas = document.getElementById('the-canvas');
// Fetch canvas' 2d context
var context = canvas.getContext('2d');
// Set dimensions to Canvas
canvas.height = viewport.height;
canvas.width = viewport.width;
// Prepare object needed by render method
var renderContext = {
canvasContext: context,
viewport: viewport
};
// Render PDF page
page.render(renderContext);
});
There are two things you can do. I tested and somehow it worked, but you will get a bigger memory consumption.
1 . Go to pdf.js and change the parameter MAX_GROUP_SIZE to like 8192 (double it for example) . Be sure to have your browser cache disable while testing.
You can force the getViewport to retrieve the image in better quality but like, I don't know how to say in English, compress it so a smaller size while showing:
// URL of PDF document
var url = "https://www.myFilePath/1Mpublic.pdf";
// Asynchronous download PDF
PDFJS.getDocument(url)
.then(function(pdf) {
return pdf.getPage(1);
})
.then(function(page) {
// Set scale (zoom) level
var scale = 1.2;
// Get viewport (dimensions)
var viewport = page.getViewport(scale);
// Get canvas#the-canvas
var canvas = document.getElementById('the-canvas');
// Fetch canvas' 2d context
var context = canvas.getContext('2d');
// Set dimensions to Canvas
var resolution = 2 ; // for example
canvas.height = resolution*viewport.height; //actual size
canvas.width = resolution*viewport.width;
canvas.style.height = viewport.height; //showing size will be smaller size
canvas.style .width = viewport.width;
// Prepare object needed by render method
var renderContext = {
canvasContext: context,
viewport: viewport,
transform: [resolution, 0, 0, resolution, 0, 0] // force it bigger size
};
// Render PDF page
page.render(renderContext);
});
enter code here
Hope it helps!
This code will help you, my issue was pdf was not rendering in crisp quality according with the responsiveness. So i searched , and modified my code like this. Now it works for rendering crisp and clear pdf according to the div size you want to give. `var loadingTask = pdfjsLib.getDocument("your_pdfurl");
loadingTask.promise.then(function(pdf) {
console.log('PDF loaded');
// Fetch the first page
var pageNumber = 1;
pdf.getPage(pageNumber).then(function(page) {
console.log('Page loaded');
var container = document.getElementById("container") //Container of the body
var wrapper = document.getElementById("wrapper");//render your pdf inside a div called wrapper
var canvas = document.getElementById('pdf');
var context = canvas.getContext('2d');
const pageWidthScale = container.clientWidth / page.view[2];
const pageHeightScale = container.clientHeight / page.view[3];
var scales = { 1: 3.2, 2: 4 },
defaultScale = 4,
scale = scales[window.devicePixelRatio] || defaultScale;
var viewport = page.getViewport({ scale: scale });
canvas.height = viewport.height;
canvas.width = viewport.width;
var displayWidth = Math.min(pageWidthScale, pageHeightScale);;
canvas.style.width = `${(viewport.width * displayWidth) / scale}px`;
canvas.style.height = `${(viewport.height * displayWidth) / scale}px`;
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
var renderTask = page.render(renderContext);
renderTask.promise.then(function() {
console.log('Page rendered');
});
});
}, function(reason) {
// PDF loading error
console.error(reason);
});`

Adding "SCNNode" to ScreenView to current view of the camera

I've got an image that im trying to add to the current camera location of the Screenview ARSCNView session. Whatever I do it seems to put the image behind the current location of the phone camera and i have to move it back to see it. I have the following code:
var currentFrame = SceneView.Session.CurrentFrame;
if (currentFrame == null) return;
var threeVector = new SCNVector3(currentFrame.Camera.Transform.Column3.X + 0.005f,
currentFrame.Camera.Transform.Column3.Y - 0.02f,
currentFrame.Camera.Transform.Column3.Z - 0.05f);
var scaleFactor = imgToAdd.Size.Width / 0.05;
float width = float.Parse((imgToAdd.Size.Width / scaleFactor).ToString());
float height = float.Parse((imgToAdd.Size.Height / scaleFactor).ToString());
var box = new SCNPlane {
Width = width,
Height = height
};
var cubeNode = new SCNNode {
Position = threeVector,
Geometry = box
};
var mat = new SCNMaterial();
mat.Diffuse.Contents = imgToAdd;
mat.LocksAmbientWithDiffuse = true;
cubeNode.Geometry.Materials = new[] { mat };
SceneView.Scene.RootNode.AddChildNode(cubeNode);
Just trying to execute a simple idea of the ARKit paint apps that you see all over the app store now. So the imgToAdd is a snapshot of the scribble that the user has already put onto the screen and this event is fired at the touchended after an image is created from the view scribbled on.
Any ideas on what I need to change to get it to line up with the current view of the camera on the phone?

Why does the text been stretched when I rotate iPhone to landscape orientation?

I wrote the UIView descendant in Xamarin and overrided Draw method.
Among other staff it draws text. It looks good in any screen orientation, but when I rotate device to the other orientation the text is stretched or shrinks.
The drawing code is:
public override void Draw(CGRect rect)
{
base.Draw(rect);
var ss = new UIStringAttributes();
ss.ForegroundColor = mTextColor;
ss.Font = mTextFont;
ss.ParagraphStyle = new NSMutableParagraphStyle() { Alignment = UITextAlignment.Center };
using (var g = UIGraphics.GetCurrentContext())
{
for (nint i = 0; i < mBars.Length; ++i)
{
var r = mBars[i];
g.SetFillColor(i + 1 == mBars.Length ? mLastBarColor : mBarColor);
g.FillRect(r);
var ns = new NSAttributedString(mDataSource[i].Item2, ss);
var textRect = new CGRect(r.Left, r.Bottom, r.Width, mTextRectHeight);
ns.DrawString(textRect);
}
}
}
Russell is right. I didn't know about UIView contentMode.
It has to be set to 'redraw'

how to add some text to a static image programatically, Using MonoTouch?

I have a static image of size 1024*768 with some logo on one side,
i want to have some text added to that image eg: Page 1, (on another side)
i got some code from
public override void ViewDidLoad ()
{
try {
base.ViewDidLoad ();
UIImage ii = new UIImage (Path.Combine (NSBundle.MainBundle.BundleUrl.ToString ().Replace ("%20", " ").Replace ("file://", ""), "images2.png"));
RectangleF wholeImageRect = new RectangleF (0, 0, ii.CGImage.Width, ii.CGImage.Height);
imageView = new UIImageView (wholeImageRect);
this.View.AddSubview (imageView);
imageView.Image = DrawVerticalText ("Trail Text", 100, 100);
Console.Write ("Switch to Simulator now to see ");
Console.WriteLine ("some stupid graphics tricks");
} catch (Exception ex) {
}
}
public static UIImage DrawVerticalText (string text, int width, int height)
{
try {
float centerX = width / 2;
float centerY = height / 2;
//Create the graphics context
byte[] mybyteArray;
CGImage tt = null;
UIImage ii = new UIImage (Path.Combine (NSBundle.MainBundle.BundleUrl.ToString ().Replace ("%20", " ").Replace ("file://", ""), "images2.png"));
using (NSData imagedata = ii.AsPNG ()) {
mybyteArray = new byte[imagedata.Length];
System.Runtime.InteropServices.Marshal.Copy (imagedata.Bytes, mybyteArray, 0, Convert.ToInt32 (imagedata.Length));
using (CGBitmapContext ctx = new CGBitmapContext (mybyteArray, width, height, 8, 4 * width, CGColorSpace.CreateDeviceRGB (), CGImageAlphaInfo.PremultipliedFirst)) {
//Set the font
ctx.SelectFont ("Arial", 16f, CGTextEncoding.MacRoman);
//Measure the text's width - This involves drawing an invisible string to calculate the X position difference
float start, end, textWidth;
//Get the texts current position
start = ctx.TextPosition.X;
//Set the drawing mode to invisible
ctx.SetTextDrawingMode (CGTextDrawingMode.Invisible);
//Draw the text at the current position
ctx.ShowText (text);
//Get the end position
end = ctx.TextPosition.X;
//Subtract start from end to get the text's width
textWidth = end - start;
//Set the fill color to blue
ctx.SetRGBFillColor (0f, 0f, 1f, 1f);
//Set the drawing mode back to something that will actually draw Fill for example
ctx.SetTextDrawingMode (CGTextDrawingMode.Fill);
//Set the text rotation to 90 degrees - Vertical from bottom to top.
ctx.TextMatrix = CGAffineTransform.MakeRotation ((float)(360 * 0.01745329f));
//Draw the text at the center of the image.
ctx.ShowTextAtPoint (2, 2, text);
tt = ctx.ToImage ();
}
}
//Return the image
return UIImage.FromImage (tt);
} catch (Exception ex) {
return new UIImage (Path.Combine (NSBundle.MainBundle.BundleUrl.ToString ().Replace ("%20", " ").Replace ("file://", ""), "images2.png"));
}
}
the output i am getting as following
As you can see it gets completely stretched in terms of width, i need this to be solved Any suggestions ???
At the same time the original image has nothing in the upper part, where as after processing it shows multi coloured layer, how to fix that ??
Why do you not draw your text directly to the image? Perhaps you can try this:
private static UIImage PutTextOnImage(UIImage image, string text, float x, float y)
{
UIGraphics.BeginImageContext(new CGSize(image.Size.Width, image.Size.Height));
using (CGContext context = UIGraphics.GetCurrentContext())
{
// Copy original image
var rect = new CGRect(0, 0, image.Size.Width, image.Size.Height);
context.SetFillColor(UIColor.Black.CGColor);
image.Draw(rect);
// Use ScaleCTM to correct upside-down imaging
context.ScaleCTM(1f, -1f);
// Set the fill color for the text
context.SetTextDrawingMode(CGTextDrawingMode.Fill);
context.SetFillColor(UIColor.FromRGB(255, 0, 0).CGColor);
// Draw the text with textSize
var textSize = 20f;
context.SelectFont("Arial", textSize, CGTextEncoding.MacRoman);
context.ShowTextAtPoint(x, y, text);
}
// Get the resulting image from context
var resultImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return resultImage;
}
The above method draws your text at coords x, y with given color and textsize. If you want it vertically you need to rotate the text with rotateCTM. keep in mind rotateCTM uses radius.
Add this to your using Context block (before DrawTextAtPoint):
var angle = 90;
var radius = 90 * (nfloat)Math.PI / 180;
context.RotateCTM(radius);

Resources