1

UIImage will be added to UIImageView in run time, I want to determine the location that user tap on image.

Is there any way to determine what is the pixel (x,y) of tapped image?

for exam:

If I have apple.png by image size of x:1000, y:1000 and I tap on exactly center of it, it should return x: 500, y: 500

I need tapped point pixels on real image (UIImageView.image) not UIImageView

Vahid
  • 3,352
  • 2
  • 34
  • 42
  • Are you using `.scaleAspectFit`? If so, you need to have three things: (1) The *point* location of the tap, (2) the actual `CGRect` of the displayed image - note, *not* the `CGRect` of the image view, and finally (3) a calculation/formula to convert between the two. I have a `UIImage` extension that should help you with the second. It returns a "scale factor" of the image view's rendering of the image, whether it's been resized up or down. I'm just not sure if it will behave properly with other `.contentModes`. –  Jan 28 '18 at 14:43
  • Yes @dfd, I use `scaleAspectFit`. I already have point of tapped location, but It's based on x,y of screen. – Vahid Jan 28 '18 at 15:16
  • On more thinking, I can only get you partway there at best. My extension will give you the *scaled point* location of the image, but I'm thinking you want something much different. For that I believe you'll need to research `CGImage` or Core Graphics. A quick search yielded a protected question (from 2009, in Obj-C) that may give you a start: https://stackoverflow.com/questions/448125/how-to-get-pixel-data-from-a-uiimage-cocoa-touch-or-cgimage-core-graphics –  Jan 28 '18 at 15:39

2 Answers2

6

Yes, by adding a UITapGestureRecognizer to the image view, getting the tap position from that and converting the tap coordinates to the image coordinates:

let img = UIImage(named: "whatever")

// add a tap recognizer to the image view
let tap = UITapGestureRecognizer(target: self, 
             action: #selector(self.tapGesture(_:)))
imgView.addGestureRecognizer(tap)
imgView.isUserInteractionEnabled = true
imgView.image = img
imgView.contentMode = .scaledAspectFit

func convertTapToImg(_ point: CGPoint) -> CGPoint? {
    let xRatio = imgView.frame.width / img.size.width
    let yRatio = imgView.frame.height / img.size.height
    let ratio = min(xRatio, yRatio)

    let imgWidth = img.size.width * ratio
    let imgHeight = img.size.height * ratio

    var tap = point
    var borderWidth: CGFloat = 0
    var borderHeight: CGFloat = 0
    // detect border
    if ratio == yRatio {
        // border is left and right
        borderWidth = (imgView.frame.size.width - imgWidth) / 2
        if point.x < borderWidth || point.x > borderWidth + imgWidth {
            return nil
        }
        tap.x -= borderWidth
    } else {
        // border is top and bottom
        borderHeight = (imgView.frame.size.height - imgHeight) / 2
        if point.y < borderHeight || point.y > borderHeight + imgHeight {
            return nil
        }
        tap.y -= borderHeight
    }

    let xScale = tap.x / (imgView.frame.width - 2 * borderWidth)
    let yScale = tap.y / (imgView.frame.height - 2 * borderHeight)
    let pixelX = img.size.width * xScale
    let pixelY = img.size.height * yScale
    return CGPoint(x: pixelX, y: pixelY)
}

@objc func tapGesture(_ gesture: UITapGestureRecognizer) {
    let point = gesture.location(in: imgView)
    let imgPoint = convertTapToImg(point)
    print("tap: \(point) -> img \(imgPoint)")
}
Sohil R. Memon
  • 9,404
  • 1
  • 31
  • 57
Gereon
  • 17,258
  • 4
  • 42
  • 73
  • I think the OP wants the *pixel* location in terms of the `UIImage.size`, not the *point* value of the `UIImageView`. –  Jan 28 '18 at 14:33
  • Thank you @Gereon, But I want the pixel location of tapped point on `UIImage`. not `UIImageView`. – Vahid Jan 28 '18 at 15:11
  • Isn't that trivial to calculate when you have a) the tap location, b) the size of the UIImage, c) the frame of the UIImageView and d) the scale factor of the screen? – Gereon Jan 28 '18 at 15:59
  • @Gereon, let me know if you know how to calculate it. – Vahid Jan 29 '18 at 05:10
  • 1
    Updated my answer – Gereon Jan 29 '18 at 10:01
0
import UIKit
import AVFoundation

class ImageViewController: UIViewController {

private lazy var imageView: UIImageView = {
    let imageView = UIImageView()
    imageView.image = UIImage(named: "Screenshot")
    imageView.bounds = CGRect(origin: .zero,
                              size: CGSize(width: view.bounds.width * 0.8,
                                           height: view.bounds.width * 0.8))
    imageView.contentMode = .scaleAspectFit
    imageView.center = view.center
    imageView.isUserInteractionEnabled = true
    imageView.backgroundColor = .blue
    return imageView
}()

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(imageView)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let imageRect = recognizeImageRect(onImageView: imageView) else { return } 
    let mockView = UIView(frame: imageView.frame)
    mockView.frame.origin.x += imageRect.origin.x
    mockView.frame.origin.y += imageRect.origin.y
    guard  let point = touches.first?.location(in: mockView) else { return }
    print(point)
}

private func recognizeImageRect(onImageView imageView: UIImageView) -> CGRect? {
    guard let imageSize = imageView.image?.size else { return nil }
    let rect = AVMakeRect(aspectRatio: imageSize, insideRect: imageView.bounds)
    return rect
}

}
  • Code-only answers are frowned upon. It's best to also include an explanation of how your code solves the issue. – HangarRash Nov 29 '22 at 17:45