Skip to content

budidino/ShittyImageCrop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ShittyImageCrop

Couldn't find anything similar online so I hacked this together. I'm not a great coder so please don't judge :) <3

All you need to do is specify the image and aspect ratio that you want. ShittyImageCrop makes sure you can zoom and pan to select the portion of the image you would like to crop

I might make this into a Pod one day when I get the time to learn how to do it :)

screenshot

Usage

Add Photo Library permission request message Privacy - Photo Library Usage Description to your Info.plist since Apple decided to crash apps that don't have it instead of displaying a generic message

Create or Copy ShittyImageCropVC.swift

import UIKit

class ShittyImageCropVC: UIViewController, UIScrollViewDelegate {
  
  var aspectW: CGFloat!
  var aspectH: CGFloat!
  var img: UIImage!
  
  var imageView: UIImageView!
  var scrollView: UIScrollView!
  
  var closeButton: UIButton!
  var cropButton: UIButton!
  
  var holeRect: CGRect!
  
  required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
  
  init(frame: CGRect, image: UIImage, aspectWidth: CGFloat, aspectHeight: CGFloat) {
    super.init(nibName: nil, bundle: nil)
    aspectW = aspectWidth
    aspectH = aspectHeight
    img = image
    view.frame = frame
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    setupView()
  }
  
  func setupView() {
    if img.imageOrientation != .up {
      UIGraphicsBeginImageContextWithOptions(img.size, false, img.scale)
      var rect = CGRect.zero
      rect.size = img.size
      img.draw(in: rect)
      img = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()
    }
    
    closeButton = UIButton(frame: CGRect(x: 40, y: view.frame.height - 40 - 90, width: 90, height: 90))
    closeButton.setImage(UIImage(named: "crop-back"), for: .normal)
    closeButton.addTarget(self, action: #selector(tappedClose), for: .touchUpInside)
    
    cropButton = UIButton(frame: CGRect(x: view.frame.width - 40 - 90, y: view.frame.height - 40 - 90, width: 90, height: 90))
    cropButton.setImage(UIImage(named: "crop-checkmark"), for: .normal)
    cropButton.addTarget(self, action: #selector(tappedCrop), for: .touchUpInside)
    
    view.backgroundColor = UIColor.gray
    
    // TODO: improve to handle super tall aspects (this one assumes full width)
    let holeWidth = view.frame.width
    print(aspectH)
    let holeHeight = holeWidth * aspectH/aspectW
    holeRect = CGRect(x: 0, y: view.frame.height/2-holeHeight/2, width: holeWidth, height: holeHeight)
    
    imageView = UIImageView(image: img)
    scrollView = UIScrollView(frame: view.bounds)
    scrollView.addSubview(imageView)
    scrollView.showsVerticalScrollIndicator = false
    scrollView.showsHorizontalScrollIndicator = false
    scrollView.alwaysBounceHorizontal = true
    scrollView.alwaysBounceVertical = true
    scrollView.delegate = self
    view.addSubview(scrollView)
    
    let minZoom = max(holeWidth / img.size.width, holeHeight / img.size.height)
    scrollView.minimumZoomScale = minZoom
    scrollView.zoomScale = minZoom
    scrollView.maximumZoomScale = minZoom*4
    
    let viewFinder = hollowView(frame: view.frame, transparentRect: holeRect)
    view.addSubview(viewFinder)
    
    view.addSubview(closeButton)
    view.addSubview(cropButton)
  }
  
  // MARK: scrollView delegate
  
  func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return imageView
  }
  
  func scrollViewDidZoom(_ scrollView: UIScrollView) {
    let gapToTheHole = view.frame.height/2-holeRect.height/2
    scrollView.contentInset = UIEdgeInsetsMake(gapToTheHole, 0, gapToTheHole, 0)
  }
  
  // MARK: actions
  
  func tappedClose() {
    print("tapped close")
    self.dismiss(animated: true, completion: nil)
  }
  
  func tappedCrop() {
    print("tapped crop")
    
    var imgX: CGFloat = 0
    if scrollView.contentOffset.x > 0 {
      imgX = scrollView.contentOffset.x / scrollView.zoomScale
    }
    
    let gapToTheHole = view.frame.height/2 - holeRect.height/2
    var imgY: CGFloat = 0
    if scrollView.contentOffset.y + gapToTheHole > 0 {
      imgY = (scrollView.contentOffset.y + gapToTheHole) / scrollView.zoomScale
    }
    
    let imgW = holeRect.width  / scrollView.zoomScale
    let imgH = holeRect.height  / scrollView.zoomScale
    
    print("IMG x: \(imgX) y: \(imgY) w: \(imgW) h: \(imgH)")
    
    let cropRect = CGRect(x: imgX, y: imgY, width: imgW, height: imgH)
    let imageRef = img.cgImage!.cropping(to: cropRect)
    let croppedImage = UIImage(cgImage: imageRef!)
    
    UIImageWriteToSavedPhotosAlbum(croppedImage, self, #selector(image(image:didFinishSavingWithError:contextInfo:)), nil)
    
    self.dismiss(animated: true, completion: nil)
  }
  
  func image(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo:UnsafeRawPointer) {
    if error == nil {
      print("saved cropped image")
    } else {
      print("error saving cropped image")
    }
  }
  
}


// MARK: hollow view class

class hollowView: UIView {
  var transparentRect: CGRect!
  
  init(frame: CGRect, transparentRect: CGRect) {
    super.init(frame: frame)
    
    self.transparentRect = transparentRect
    self.isUserInteractionEnabled = false
    self.alpha = 0.5
    self.isOpaque = false
  }
  
  required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  
  override func draw(_ rect: CGRect) {
    backgroundColor?.setFill()
    UIRectFill(rect)
    
    let holeRectIntersection = transparentRect.intersection( rect )
    
    UIColor.clear.setFill();
    UIRectFill(holeRectIntersection);
  }
}

Prompt the cropper with frame, image and aspect ratio

let shittyVC = ShittyImageCropVC(frame: (self.navigationController?.view.frame)!, image: UIImage(named:"img")!, aspectWidth: 4, aspectHeight: 3)
self.navigationController?.present(shittyVC, animated: true, completion: nil)

About

Image cropper view controller

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages