Swift ImageCropper returns an image outside of the specified "window" - swift

I am making an app that has a UIWebView along with a button on a single view controller. When the button is clicked, an image (of the UIWebView) is captured using UIGraphicsContext.
This part works great! But when the button is clicked, after capturing the image, it displays the image as a subview on the same view, and I have been trying to use an ImageCropper Library that draws a CGRect in another subview over the UIImageView on the screen with a submit button. The rectangle itself can be resized (dragging the corners/edges) and moved around the view.
When the submit button is clicked, another subview is displayed in the top left hand portion of screen and display the image that was cropped (after clicking submit button) The idea is to only capture what is inside the rectangle. I am able to get the code working but the image captured is of the same image but not a section that is inside the CGRect.
I have 3 images that show how it works and shows the image that is cropped incorrectly.enter image description here . Shot 1 . Shot 2
Shot 3. I believe my problems lies within the size of image captured and the size of the image with the crop rect are not equal and that is why it is distorting it.
Does anyone know what might be the cause? Sorry for the long winded question but any help would be greatly appreciated!
Here is my code below:
ViewController.swift:
class ViewController: UIViewController {
#IBOutlet var webView: UIWebView!
#IBOutlet var imageView: UIImageView!
override func viewDidLoad() {
imageView.isHidden = true
let aString = URL(string: "https://www.kshuntfishcamp.com/home.page")
webView.loadRequest(URLRequest(url: aString!))
super.viewDidLoad()
}
#IBAction func takePhotoPressed(_ sender: UIButton) {
UIGraphicsBeginImageContextWithOptions(webView.bounds.size, false, 0.0)
if let aContext = UIGraphicsGetCurrentContext(){
webView.layer.render(in: aContext)
}
let capturedImage:UIImage? = UIGraphicsGetImageFromCurrentImageContext()
imageView = UIImageView(frame: CGRect(x: 22, y: 123, width: 330, height: 330))
let image = capturedImage
imageView.image = image
imageView.contentMode = UIViewContentMode.scaleAspectFill
imageView.clipsToBounds = true
imageView.isHidden = true
webView.isHidden = true
let editView = EditImageView(frame: self.view.frame)
let image2 = capturedImage!
editView.initWithImage(image: image2)
let croppedImage = editView.getCroppedImage()
self.view.addSubview(editView)
self.view.backgroundColor = UIColor.clear
UIImageWriteToSavedPhotosAlbum(croppedImage, nil, nil, nil)
}
EditImageView.swift - source (https://github.com/Thanatos-L/LyEditImageView)-only including parts that seem relevant to solving the problem
func initWithImage(image:UIImage){
imageView = UIImageView(frame: CGRect(x: 22, y: 123, width: 330, height: 330))
imageView.tag = IMAGE_VIEW_TAG;
self.addSubview(self.imageView)
imageView.isUserInteractionEnabled = true;
imageView.image = image
imageView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
let frame = AVMakeRect(aspectRatio: imageView.frame.size, insideRect: self.frame);
imageView.frame = frame
originImageViewFrame = frame
NSLog("initWithImage %#", NSStringFromCGRect(originImageViewFrame))
imageZoomScale = 1.0
commitInit()
}
private func cropImage() {
let rect = self.convert(cropView.frame, to: imageView)
let imageSize = imageView.image?.size
let ratio = originImageViewFrame.size.width / (imageSize?.width)!
let zoomedRect = CGRect(x: rect.origin.x / ratio, y: rect.origin.y / ratio, width: rect.size.width / ratio, height: rect.size.height / ratio)
let croppedImage = cropImage(image: imageView.image!, toRect: zoomedRect)
var view: UIImageView? = self.viewWithTag(1301) as? UIImageView
if view == nil {
view = UIImageView()
}
view?.frame = CGRect(x: 0, y: 0, width: croppedImage.size.width , height: croppedImage.size.height)
view?.image = croppedImage
view?.tag = 1301
self.addSubview(view!)
}

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

How can I insert image to end of the string and insert more than one image in UITextView with swift?

I created an UITextView and I can add image with image picker into text view. But I have some problem about replacement image. I want add this image end of the text. And I want to add images more than one. (like: text + image + text...). How can I solve this problem ? Can anyone help me ?
let pickImage = UIImageView() // this is for imagepickercontroller
lazy var writePost: UITextView = {
let wpost = UITextView()
let images = pickImage
let attachment = NSTextAttachment()
let attString = NSAttributedString(attachment: attachment)
attachment.image = images.image
images.frame = CGRect(x: 0, y: 0, width: 220, height: 220)
images.contentMode = .scaleAspectFill
wpost.textStorage.insert(attString, at: wpost.selectedRange.location)
wpost.addSubview(images)
wpost.textAlignment = .center
wpost.textColor = UIColor.lightGray
wpost.font = UIFont(name: "AvenirNext-DemiBoldItalic", size: 16)
wpost.isEditable = true
wpost.isScrollEnabled = true
wpost.layer.borderWidth = 1.5
wpost.layer.cornerRadius = 7.0
wpost.layer.borderColor = UIColor.orange.cgColor
wpost.delegate = self
return wpost
}()
What you should do is use UITextView's textContainer's exclusionPaths property. The exclusionPaths property lets you assign an array of UIBezierPaths to your textContainer. When exclusionPaths are set, none of the UITextView's text will appear within these paths. You could then add a UIImageView as a subview of the UITextView's super view placed above the UITextView that has a frame equal to said exclusion path.
The end result will be a UITextView with a UIImageView placed above it. None of the UITextView's text will be blocked by the UIImageView as the UITextView's textContainer's exclusionPaths have instructed the text not to populate there.
Here is an example of some code I've done to do something similar, with variable names changed to match your code a bit:
let imageView: UIImageView!
func addImageView() {
imageView = UIImageView(frame: CGRect(x: textView.frame.maxX - 200, y: textView.frame.maxY - 150, width: 200, height: 150))
textView.superView.addSubview(imageView)
}
func setExclusionPath(for imageView: UIImageView) {
let imageViewPath = UIBezierPath(rect: CGRect(x: textView.frame.maxX - imageView.frame.width, y: textView.frame.maxY - imageView.frame.height, width: imageView.frame.width, height: imageView.frame.height))
textView.textContainer.exclusionPaths.append(imageViewPath)
}
func someMethod() {
addImageView()
setExclusionPath(for: self.imageView)
}
Resources:
exclusionPaths reference from Apple

How to change the size of the titleView of Navigation Item? (Swift)

In my app I want to change the titleView of my Navigation Controller to a custom ImageView but I have a very strange problem. I basically just can`t set the frame of my ImageView.
This is my code:
func NavBarEinrichten(){
let logo = UIImage(named: "Notennamen für Griffe 1")!
let imageView = UIImageView(image:logo)
imageView.contentMode = .scaleAspectFit
//To show the problem better
imageView.backgroundColor = UIColor.red
let navController = navigationController!
let bannerWidth:CGFloat = 20
let bannerHeight:CGFloat = navController.navigationBar.frame.size.height
let bannerX = bannerWidth / 2 - logo.size.width / 2
let bannerY = bannerHeight / 2 - logo.size.height / 2
imageView.frame = CGRect(x: bannerX, y: bannerY, width: bannerWidth, height: bannerHeight)
self.navigationItem.titleView?.frame = CGRect(x: bannerX, y: bannerY, width: bannerWidth, height: bannerHeight)
navigationItem.titleView = imageView
print(imageView.frame, "imageView frame")
}
The print statement prints:
(0.0, 0.0, 357.0, 142.0) imageView frame
Which is right, because it looks like this when i run it, but not like it should be. Because I want the width to be 20.
In the simulator it looks like this:
I tried to call this method in viewDidLoad(), viewWillAppear()and viewDidLayoutSubviews()
Every time with the exact same outcome...
Can you help me?

Facebook Picture zoom animation in swift, picture not showing the initial position

I've been trying to make a zoom animation like Facebook when you click a picture into a cell to move into the middle of the screen. The animation works, but for a reason that I can not figure out, it is not starting from the initial position it is giving me another frame. Please help, I've been struggling with this for a few days now.
I am using a collectionView with CustomCell and everything it's done programmatically:
The function in CenterVC:
//MARK: Function to animate Image View (it will animate to the middle of the View)
func animateImageView(statusImageView : UIImageView) {
//Get access to a starting frame
statusImageView.frame.origin.x = 0
if let startingFrame = statusImageView.superview?.convert(statusImageView.frame, to: nil) {
//Add the view from cell to the main view
let zoomImageView = UIView()
zoomImageView.backgroundColor = .red
zoomImageView.frame = statusImageView.frame
view.addSubview(zoomImageView)
print("Starting frame is: \(startingFrame)")
print("Image view frame is: \(statusImageView.frame)")
UIView.animate(withDuration: 0.75, animations: {
let height = (self.view.frame.width / startingFrame.width) * startingFrame.height
let y = self.view.frame.height / 2 - (height / 2)
zoomImageView.frame = CGRect(x: 0, y: y, width: self.view.frame.width, height: height)
})
}
}
This is the pictureView inside the cell and the constraints (this is where I am setting up the picture for the view, and I am using in cellForRowAtIndexPath cell.centerVC = self):
var centerVC : CenterVC?
func animate() {
centerVC?.animateImageView(statusImageView: pictureView)
}
let pictureView : UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.image = UIImage(named: "cat")
imageView.backgroundColor = UIColor.clear
imageView.layer.masksToBounds = true
imageView.isUserInteractionEnabled = true
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
addConstraintsWithFormat(format: "V:|-5-[v0(40)]-5-[v1]-5-[v2(200)]", views: profileImage, postTextView, pictureView)
addConstraintsWithFormat(format: "H:|[v0]|", views: pictureView)
This is what it prints out in the debugger:
Starting frame is: (5.0, 547.5, 365.0, 200.0)
Image view frame is: (0.0, 195.5, 365.0, 200.0)
As you can see the starting frame it's different from the initial frame and position of the picture. The animation it's not leaving the initial position it just appears somewhere on top and animates to the middle. I don't know what to do, please advice.
For some reason, it was not reading the starting frame rect so I've made a new CGRect that gets the origin and set the size of the picture: (it animates perfectly now)
zoomImageView.frame = CGRect(origin: startingFrame.origin, size: CGSize(width: view.frame.width, height: statusImageView.frame.height))

Can't fit UIImage inside UIScrollview

I'm using this code to pick an image from gallery and fit it inside a UIScrollView:
let imageView = UIImageView(image: inputImageDelegate!)
imageView.frame = CGRect(x: 0, y: 0, width: contentView.frame.width, height: contentView.frame.height)
imageView.contentMode = UIViewContentMode.ScaleAspectFill
inputImage = imageView
contentView.addSubview(imageView)
self.scrollView.minimumZoomScale = 0.5
self.scrollView.maximumZoomScale = 6.0
self.scrollView.contentSize = self.inputImage.frame.size
self.scrollView.delegate = self
self.scrollView.zoomScale = 1
imageView.sizeToFit()
Result:
I want to make image's width equal to the screen width after loading (user can scale it up and down). Neither of self.scrollView.zoomScale = 1 nor imageView.sizeToFit() worked.
I'll assume you have your scroll view connected to an IBOutlet like this:
#IBOutlet weak var scrollView: UIScrollView!
You should set the frame of the UIImageView based on the UIScrollView's contentSize property.
let imageView = UIImageView(image: inputImageDelegate!)
let contentSize = self.scrollView.contentSize // convenience
imageView.frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
...