64

I would like to achieve :

  • Scale to fill mode of image view
  • Insets to UIImageView so that image does not meet the edges of the UIImageView

Bottomline: I want to increase the touch area of the UIImageView without stretching the image beyond certain size.

Is it possible through storyboard ? If not can anyone tell how to do it programmatically ?

I can achieve similar functionality through a UIButton and setting image inset through storyboard but I can't find any such thing with UIImageView.

Sarasranglt
  • 3,819
  • 4
  • 23
  • 36
  • Are you considering changing the size of the image while in runtime without actually affecting the original image? will that do? – Saheb Roy Aug 31 '15 at 05:28
  • That would change the scale and pixelate your image if your imageview is low, instead there is a turnaround which you can try, if u are willing to reconsider i can post an answer – Saheb Roy Aug 31 '15 at 05:40
  • you can post I'll see if it works .. – Sarasranglt Aug 31 '15 at 05:42

8 Answers8

90

You can add inset to UIImage instead of UIImageView.

Swift 4

let imageView = UIImageView() 
imageView.contentMode = .scaleAspectFill
imageView.image = UIImage(named: "example")?.withAlignmentRectInsets(UIEdgeInsets(top: -4, left: 0, bottom: -4, right: 0))
User
  • 31,811
  • 40
  • 131
  • 232
BumMo Koo
  • 1,119
  • 1
  • 8
  • 11
  • 10
    I corrected the syntax but this actually doesn't work – User Feb 17 '18 at 21:51
  • 5
    Works super well. I found I had to put negative insets to make my image smaller though. – Ulises Giacoman Aug 16 '18 at 17:03
  • 2
    Looks good on paper, but has no effect for me, whether negative or positive values for the `UIEdgeInset` arguments. And yes, I'm using contentMode `.scaleAspectFill`. – ScottyBlades Aug 23 '18 at 23:47
  • 4
    I just tested it again, it appears it has no effect when you are setting `imageView` frame manually. It works when `imageView` is set using auto layout. – BumMo Koo Aug 25 '18 at 00:21
  • I'm using constraints. – ScottyBlades Aug 27 '18 at 01:00
  • This will change the UIImageView's constraints. – Ennabah Feb 25 '19 at 22:26
  • 4
    I ran in the problem that this solution didnt work for me. The problem was that my image had top, bottom, trailing and leading constrains to its parent. The fix was to have only "align center x" and "align center y" contains to be available for this imageView. So the imageView can compute its own instrict size, taking the insets into count. – Paul Reznik Mar 21 '19 at 13:46
  • This has strange behavior for my use case. I wanted a rounded grey square background (48px) with an inset image in the center (24px). I was using constraints for layout. For some reason this messes with the corner radius and background size. Would recommend just using a UIView and embedded UIImageView rather than messing with this property. – Dominic Holmes Jan 18 '22 at 20:36
33

Using the method given here :

UIImage *image= [MyUtil imageWithImage:[UIImage imageNamed:@"MyImage"] scaledToSize:CGSizeMake(80, 80)];

UIImageView* imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imgView.contentMode = UIViewContentModeCenter;
[imgView setImage:image];

Now your insets become 100-80 i.e. 20 .

This is a workaround to provide insets to an UIImageView. Better answers are welcomed.

Community
  • 1
  • 1
Sarasranglt
  • 3,819
  • 4
  • 23
  • 36
28

It looks like you want to have a touchable imageView, so why not using an UIButton with image and imageEdgeInsets?

UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
[b setFrame:someFrame];
[b setImage:img forState:UIControlStateNormal];
b.imageEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);
[b addTarget:self action:@selector(imageClicked:) forControlEvents:UIControlEventTouchUpInside];
mmackh
  • 3,550
  • 3
  • 35
  • 51
MarkHim
  • 5,686
  • 5
  • 32
  • 64
26

My Code

  • minus inset: smaller image
  • plus inset: larger image

Swift4

private lazy var imageView: UIImageView = {
    let view = UIImageView()
    view.contentMode = .scaleAspectFit
    let insetValue: CGFloat = -8
    view.image = image.withAlignmentRectInsets(UIEdgeInsets(top: insetValue, left: insetValue, bottom: insetValue, right: insetValue))
    return view
}()
shtnkgm
  • 1,396
  • 15
  • 17
20

Do not suffer, try this:

After trying for a while I found this solution:

extension UIImage {

    func withInset(_ insets: UIEdgeInsets) -> UIImage? {
        let cgSize = CGSize(width: self.size.width + insets.left * self.scale + insets.right * self.scale,
                            height: self.size.height + insets.top * self.scale + insets.bottom * self.scale)

        UIGraphicsBeginImageContextWithOptions(cgSize, false, self.scale)
        defer { UIGraphicsEndImageContext() }

        let origin = CGPoint(x: insets.left * self.scale, y: insets.top * self.scale)
        self.draw(at: origin)

        return UIGraphicsGetImageFromCurrentImageContext()?.withRenderingMode(self.renderingMode)
    }
}

Usage example:

let margin:CGFloat = 16
self.imageView.image = image?.withInset(UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin))

Source

Pedro Trujillo
  • 1,559
  • 18
  • 19
9

One option is to use withAlignmentRectInsets, but it doesn't work with the layer border.

Another way is to use resizableImage(withCapInsets: resizingMode: .stretch) where you'd want to use positive inset values for smaller images.

Sample code:

private func setupQRImageView() -> UIImageView {
    let imageView = UIImageView()
    imageView.contentMode = .scaleAspectFit
    imageView.layer.borderColor = Default.imageBorderColor.cgColor
    imageView.layer.borderWidth = 4.0
    imageView.layer.cornerRadius = 6.0
    let imageInset: CGFloat = 8.0
    imageView.image = UIImage(named: "QR")?.resizableImage(withCapInsets: UIEdgeInsets(top: imageInset, left: imageInset, bottom: imageInset, right: imageInset), resizingMode: .stretch)
    return imageView
}

Produces the following result:

UIImageView with a border and padded inset

Nikolay Derkach
  • 1,734
  • 2
  • 22
  • 34
5

You can add a UIView first and then you add your UIImageView inside the UIView with a padding of whatever you like, and if you want your gesture to get affected only in UIImageView, make it UserInteractionEnable to yes or if you want the gesture to be in the whole then you can add the gesture to the whole UIView

Saheb Roy
  • 5,899
  • 3
  • 23
  • 35
5

Try work around with UIImage within UIView like this ;

enter image description here

Ionz
  • 97
  • 1
  • 4