How to get the displayed image frame from UIImageView?
Asked Answered
C

6

38

I modify the UIImageView's contentMode property. So my image's frame is not equal to the frame of UIImageView. My problem is how to retrive the image's frame from UIImageView.

As the following example, the orange rectangle is the UIImageView's border. It is obviously that the frame of image is not the same as UIImageView.

Figure 1

Chymotrypsin answered 17/1, 2011 at 9:25 Comment(0)
A
35

If it's UIViewContentModeScaleAspectFit, it's something like this:

UIImageView *iv; // your image view
CGSize imageSize = iv.image.size;
CGFloat imageScale = fminf(CGRectGetWidth(iv.bounds)/imageSize.width, CGRectGetHeight(iv.bounds)/imageSize.height);
CGSize scaledImageSize = CGSizeMake(imageSize.width*imageScale, imageSize.height*imageScale);
CGRect imageFrame = CGRectMake(roundf(0.5f*(CGRectGetWidth(iv.bounds)-scaledImageSize.width)), roundf(0.5f*(CGRectGetHeight(iv.bounds)-scaledImageSize.height)), roundf(scaledImageSize.width), roundf(scaledImageSize.height));
Accuracy answered 17/1, 2011 at 9:43 Comment(10)
So, UIImageView has no direct method to retrieve the frame of image. It needs to be calculated manually, right?Chymotrypsin
Yes. In fact, it doesn't even seem to have a private method that returns the frame of the image based on contentMode. I guess the calculations are performed in -drawRect: and -sizeThatFits:.Accuracy
I tried sizeThatFits:, it cannot retrieve the frame of image. I changed the implementation about UIImageView. If I have to calculate the imageFrame by self, I will prefer the contentMode UIViewContentModeScaleToFill.Chymotrypsin
This works, but you should remove the floorf functions from CGRectMake. They aren't necessary (CGrect takes CGFloats) and it's best to let iOS handle the rounding. My sizes were occasionally off by a pixel until I removed floorf.April
My sizes were occasionally off by a pixel until I added floorf to the image frame sizes also. So the last line should be (for me at least): CGRect imageFrame = CGRectMake(floorf(0.5f*(CGRectGetWidth(iv.bounds)-scaledImageSize.width)), floorf(0.5f*(CGRectGetHeight(iv.bounds)-scaledImageSize.height)), floorf(scaledImageSize.width), floorf(scaledImageSize.height));Curling
@AlexeyGolikov Thanks, I corrected the code. I also think that roundf() should be more appropriate than floorf(), but I'm not sure, to be honest.Accuracy
you can use this code in swift with round() fmin() functionsGautier
Zooming imageview in UIScrollView chnages the frame.Hordein
How do you process when it's not UIViewContentModeScaleAspectFit ?Mackie
import AVKit let rect = AVMakeRect(aspectRatio: image.size, insideRect: containerView.bounds);Hyperaesthesia
D
32

In fact, it's built-in since iOS 4 (for UIViewContentModeScaleAspectFit):

@import AVFoundation;

AVMakeRectWithAspectRatioInsideRect(imageView.image.size, imageView.bounds);

Since frame components may be not round numbers, consider wrapping the call by

CGRectIntegral(…)

See http://nshipster.com/image-resizing/ for more tricks.

Detrain answered 29/4, 2015 at 12:48 Comment(1)
Instead of imageView.bound i had to pass imageView.frameTacye
S
12

Use this api from AVFoundation

#import <AVFoundation/AVFoundation.h>

CGRect imageRect = 
    AVMakeRectWithAspectRatioInsideRect(imageView.image.size, imageView.bounds);
Steiger answered 8/11, 2016 at 9:24 Comment(0)
H
6

Its very easy now

import AVKit
    
let rect = AVMakeRect(aspectRatio: image.size, insideRect: containerView.bounds);
Hyperaesthesia answered 24/9, 2020 at 9:21 Comment(0)
J
4
 if you want to calculate it yourself, use below code written by me.

func getImageFrameInImageView(imageView : UIImageView) -> CGRect {
    /*
    by 徐明刚, [email protected]
    算法:
        设高宽比 r = h/w, 则:r(imageView) 缩写成r(v), r(image)缩写为r(i), 
        if r(i) > r(v), 则
        h(i) = h(v), 
        h(i) / w(i) = r(i) -> w(i) = h(i) / r(i)
        y = 0
        x = (w(v) / 2)  - (w(i) / 2)
    反之
    */

    let image = imageView.image!
    let wi = image.size.width
    let hi = image.size.height
    print("wi:\(wi), hi:\(hi)")

    let wv = imageView.frame.width
    let hv = imageView.frame.height
    print("wv:\(wv), hv:\(hv)")

    let ri = hi / wi
    let rv = hv / wv
    print("ri:\(ri), rv:\(rv)")

    var x, y, w, h: CGFloat

    if ri > rv {
        h = hv
        w = h / ri
        x = (wv / 2) - (w / 2)
        y = 0
    } else {
        w = wv
        h = w * ri
        x = 0
        y = (hv / 2) - (h / 2)
    }

    return CGRect(x: x, y: y, width: w, height: h)
}

To test if the function is right or not, I've used another view identifying the border of the image in UIImageView.

    let frameIdentifyBorderView = UIView(frame: imageFrameInImageView)
    frameIdentifyBorderView.layer.borderWidth = 3
    frameIdentifyBorderView.layer.borderColor = UIColor.blueColor().CGColor

    imageView.addSubview(frameIdentifyBorderView)

enter image description here

perfect!

Jamie answered 18/12, 2015 at 3:25 Comment(1)
Super, I appreciate. And it is even better as a imageFrame property in extension of UIImageView.Mannuela
T
3

The easiest way is to use AVMakeRectWithAspectRatioInsideRect. Just import AVFoundation and you can get the rect in a single line of Code. Swift 4. Be sure to import AVFoundation.

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var imageView = UIImageView()
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        imageView.frame = self.view.bounds
        imageView.contentMode = .scaleAspectFit
        imageView.backgroundColor = .blue
        imageView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
        self.view.addSubview(imageView)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        URLSession.shared.dataTask(with: URL(string: "https://images.pexels.com/photos/248797/pexels-photo-248797.jpeg?w=940&h=650&dpr=2&auto=compress&cs=tinysrgb")!) { (data, response, error) in
            guard let dt = data else{print("error");return}
            DispatchQueue.main.async {
                guard let image = UIImage(data: dt) else{print("error");return}
                self.imageView.image = image
                self.imageView.layoutIfNeeded()
                let realImageRect = AVMakeRect(aspectRatio: image.size, insideRect: self.imageView.bounds)


                //proof
                let view = UIView(frame: realImageRect)
                view.layer.borderColor = UIColor.green.cgColor
                view.layer.borderWidth = 3
                self.view.addSubview(view)
            }
        }.resume()
    }
}
Thorlie answered 19/3, 2018 at 1:11 Comment(1)
I think it should be let realImageRect = AVMakeRect(aspectRatio: image.size, insideRect: self.imageView.frame). bounds returns size without x and y relative to superview.Nicker

© 2022 - 2024 — McMap. All rights reserved.