WKWebView Swift - Make screenshot of website without the fixed menu bar - swift

I'm using Xcode Swift 3 and WKWebView to display the websites. Then I try to screen capture the website using the following code that I've found from the Internet. There are some websites that come with a fixed menu bar either at the top or bottom of the website. Hence, as I scroll the website, the menu bar does not move with the browser and is fixed on the screen. The problem is this fixed menu bar of the website appears on my screenshot. Is there a way to make a screenshot without the fixed menu bar? Thanks in advance for your help.
func snapshot(of rect: CGRect? = nil) -> UIImage? {
// snapshot entire view
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: true)
let wholeImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// if no `rect` provided, return image of whole view
guard let image = wholeImage, let rect = rect else { return wholeImage }
// otherwise, grab specified `rect` of image
let scale = image.scale
let scaledRect = CGRect(x: rect.origin.x * scale, y: rect.origin.y * scale, width: rect.size.width * scale, height: rect.size.height * scale)
guard let cgImage = image.cgImage?.cropping(to: scaledRect) else { return nil }
return UIImage(cgImage: cgImage, scale: scale, orientation: .up)
}

Related

Cropping CGRect from AVCapturePhotoOutput (resizeAspectFill)

I have found the following problem and unfortunatly other posts have not helped me to a working solution.
I have a simple app that shows the camera preview (AVCaptureVideoPreviewLayer) where the video gravity has been set to resizeAspectFill (videoGravity = .resizeAspectFill).
From my understanding this only streches the image in the width to make to fill the screen.
On my preview layer I also have applied a CGRect as a mask with fixed x, y, width and height.
Now once I take a photo i'm trying to crop that exact rectangle out of the image. For my understanding i'm supposed to use some kind of math to convert the CGRect to the same aspect ratio as the image that I get from the AVCapturePhotoOutput method but it never seems to crop correctly in the width.
private func cropImage(image: UIImage) {
let rect = CGRect(x: 25, y: 150, width: 325, height: 230)
let scale = CGAffineTransform(scaleX: 1/self.view.frame.width, y: 1/self.view.frame.height)
let flip = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -1)
let bounds = rect.applying(scale).applying(flip)
let topLeft = bounds.topLeft.scaled(to: image.size)
let topRight = bounds.topRight.scaled(to: image.size)
let bottomLeft = bounds.bottomLeft.scaled(to: image.size)
let bottomRight = bounds.bottomRight.scaled(to: image.size)
var ciImage = CIImage(image: image.forceSameOrientation())!
ciImage = ciImage.applyingFilter("CIPerspectiveCorrection", parameters: [
"inputTopLeft": CIVector(cgPoint: bottomLeft),
"inputTopRight": CIVector(cgPoint: bottomRight),
"inputBottomLeft": CIVector(cgPoint: topLeft),
"inputBottomRight": CIVector(cgPoint: topRight)
])
let context = CIContext()
let cgImage = context.createCGImage(ciImage, from: ciImage.extent)
let output = UIImage(cgImage: cgImage!)
let vc = PreviewViewController()
vc.imageView.image = output
self.present(vc, animated: true, completion: nil)
}
So again, basically it does crop at the correct height but its only the width that does not seem to go well.
Image example of what I would want to capture.
https://imgur.com/a/8GryEgX
As you can see the bounding box in the top left stops after the "Q" button.
Result:
https://imgur.com/FwKRWxK
As you can see in this image, it does crop correctly in the height however if we take a look at the top left it also includes half of the button to the left of the "Q" (Tab button)
Any help towards the solution would be appreciated!
I managed to solve the issue with this code.
private func cropToPreviewLayer(from originalImage: UIImage, toSizeOf rect: CGRect) -> UIImage? {
guard let cgImage = originalImage.cgImage else { return nil }
// This previewLayer is the AVCaptureVideoPreviewLayer which the resizeAspectFill and videoOrientation portrait has been set.
let outputRect = previewLayer.metadataOutputRectConverted(fromLayerRect: rect)
let width = CGFloat(cgImage.width)
let height = CGFloat(cgImage.height)
let cropRect = CGRect(x: (outputRect.origin.x * width), y: (outputRect.origin.y * height), width: (outputRect.size.width * width), height: (outputRect.size.height * height))
if let croppedCGImage = cgImage.cropping(to: cropRect) {
return UIImage(cgImage: croppedCGImage, scale: 1.0, orientation: originalImage.imageOrientation)
}
return nil
}
usage of the piece of code for my case:
let rect = CGRect(x: 25, y: 150, width: 325, height: 230)
let croppedImage = self.cropToPreviewLayer(from: image, toSizeOf: rect)
self.imageView.image = croppedImage

Crop Image so It Becomes Full Screen like Snapchat

If you upload a picture to Snapchat (that isn't already full screen), it will zoom in and crop the photo so that it becomes full screen. I am able to do this in my ImageView using autoresizing masks, but I need to be able to save the image in this cropped state and I can't figure out how to do it.
This is how I am able to display the image (selected from camera roll) in the image view how I want it
let imgView = UIImageView(image: image)
imgView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin]
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
imgView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
self.view.addSubview(imgView)
This turns a non-full screen photo and displays it full screen with the propping zoom/crop. How can I now save the photo as a full screen photo?
you can capture an image from given views.
func image(with view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
defer { UIGraphicsEndImageContext() }
if let context = UIGraphicsGetCurrentContext() {
view.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
return nil
}
let img = image(with: YourImageView)

ScreenShot issue in ARKit in swift

i have application that uses ARSCNView. i'm trying to take a screenshot on click of a button and saved that image in the gallery. But when i take a screenshot it does not show the content on that screen. Just show that image, i have some labels on it but it does not show that in an image. This is my code,
#IBAction func captureImage(_ sender: Any) {
image = sceneView.snapshot()
UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
}
How can i show that labels and buttons on ARSCView in a screenshot?
snapshot() will only take screenshot of Scene.
To take screenshot of Scene with Labels and Buttons use below method:
func snapshot(of rect: CGRect? = nil) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, self.view.isOpaque, 0)
self.view.drawHierarchy(in: self.view.bounds, afterScreenUpdates: true)
let fullImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let image = fullImage, let rect = rect else { return fullImage }
let scale = image.scale
let scaledRect = CGRect(x: rect.origin.x * scale, y: rect.origin.y * scale, width: rect.size.width * scale, height: rect.size.height * scale)
guard let cgImage = image.cgImage?.cropping(to: scaledRect) else { return nil }
return UIImage(cgImage: cgImage, scale: scale, orientation: .up)
}
To Take Screenshot:
#IBAction func takeScreenShotTapped(_ sender: Any) {
let screenShot = snapshot(of: CGRect(x: 80, y: 80, width: 100, height: 100))
}
If you want to take screenshot of fullscreen then just call snapshot().
Hope this will help you :)

Swift 4 ImageScrollView resizing visible cells in UITableView

I am using the ImageScrollView cocoa pod v1.5 inside a UITableViewCell (Swift 4).
I am downloading images from Firestore using SDWebImage (but also tried Kingfisher with no change in my issue). My images are 700x700 and are being displayed in a ImageScrollView that, depending upon device, is about 400x100. I have set the SDWebImage imageContentMode to .widthFill. I rotate the image to get it in the form I want for the tableview. I use it in it's regular orientation in other places.
The first time my cells are shown the images are shown correctly. If I go back to the previous page, then show the same results in the table again, the visible cells will have their images no longer fitting correctly with regards to width, they are now too wide. If I scroll down hiding those cells all new cells are properly displayed, when I scroll back up the incorrect cells are now displaying correctly. Happens in simulator and actual phone.
Here are the important parts of my UITableViewCell class :
class MyTableCell: UITableViewCell {
var ski : Ski!
var skiImageView: UIImageView = UIImageView()
#IBOutlet weak var skiImageScrollView: ImageScrollView!
func configureCell(ski: Ski) {
self.ski = ski
let imageUrl = ski.imageUrl!
let url = URL(string: imageUrl)
skiImageView.sd_setImage(with: url, placeholderImage: placeholderImage, options: [.retryFailed, .continueInBackground]
, completed: {
(image, error, cacheType, url) in
if (error != nil) {
print("ConfigureCell Error : \(error!)")
return;
}
let rotatedImage = self.imageRotatedByDegrees(oldImage: image!, deg: 90.0)
self.skiImageScrollView.display(image: rotatedImage) var image = self.skiImageView.image!
self.skiImageScrollView.imageContentMode = .widthFill
})
}
func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: oldImage.size.width, height: oldImage.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat.pi / 180)
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = UIGraphicsGetCurrentContext()!
//Move the origin to the middle of the image so we will rotate and scale around the center.
bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
Another strange thing is if I slap the back of my phone or drop it on my desk, the images will jump down so only parts of the top of my image are visible in the ImageScrollView, but callbacks from ImageScrollView for zooming are not activated, so no idea what is happening there either.
You might try the following:
imageScrollView.imageContentMode = .aspectFit
before
self.skiImageScrollView.display(image: rotatedImage)

Is it possible to change the size of the image you get from using the camera thru imagePicker?

When I try to change the property currently, I am getting a error that the size is a "Get Only Property." Anyone know a way around this?
you can try the following (I didn't run it yet, pretty sure it works..)
let imagePickerView: UIView = self.imagePicker.view
let cameraViewFrame: CGRect = CGRectMake(0, self.overlay.topBarHeight,
self.view.bounds.size.width,
self.view.bounds.size.height -self.overlay.topBarHeight - self.overlay.bottomBarHeight);
imagePickerView.frame = cameraViewFrame
Good luck :)
What we can do is to draw a new UIImage instead. The code below is a function to scale the image passed in. And after the image has been scaled, the size changes.
extension UIImage {
class func scaleImage(image:UIImage, scaleFloat:CGFloat) -> UIImage{
let size = CGSizeMake(image.size.width * scaleFloat, image.size.height * scaleFloat)
UIGraphicsBeginImageContext(size)
let context = UIGraphicsGetCurrentContext()
var transform = CGAffineTransformIdentity
transform = CGAffineTransformScale(transform, scaleFloat, scaleFloat)
CGContextConcatCTM(context, transform)
image.drawAtPoint(CGPointMake(0, 0))
let newimg = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newimg
}
}
Here is the example code to scale the image to 150x150. But these codes only helps the square image. If it is not square, you can test for what will happen.
extension UIImage {
class func scaleImgTo150x150(image:UIImage) -> UIImage{
let scale:CGFloat
if image.size.width > image.size.height{
scale = 150/image.size.width
}
else{
scale = 150/image.size.height
}
return UIImage.scaleImage(image, scaleFloat: scale)
}
}