9

I'm trying to display a simple NSImageView with it's image centered without scaling it like this:

Just like iOS does when you set an UIView's contentMode = **UIViewContentModeCenter**

Just like iOS does when you set an UIView's contentMode = UIViewContentModeCenter

So I tried all NSImageScaling values, this is what I get when I chose NSScaleNone

I really don't understand what's going on :-/

I really don't understand what's going on :-/

betzerra
  • 839
  • 1
  • 8
  • 17

4 Answers4

7

You can manually generate the image of the correct size and content, and set it to be the image of the NSImageView so that NSImageView doesn't need to do anything.

NSImage *newImg = [self resizeImage:sourceImage size:newSize];
[aNSImageView setImage:newImg];

The following function resizes an image to fit the new size, keeping the aspect ratio intact. If the image is smaller than the new size, it is scaled up and filled with the new frame. If the image is larger than the new size, it is downsized, and filled with the new frame

- (NSImage*) resizeImage:(NSImage*)sourceImage size:(NSSize)size{

NSRect targetFrame = NSMakeRect(0, 0, size.width, size.height);
NSImage*  targetImage = [[NSImage alloc] initWithSize:size];

NSSize sourceSize = [sourceImage size];

float ratioH = size.height/ sourceSize.height;
float ratioW = size.width / sourceSize.width;

NSRect cropRect = NSZeroRect;
if (ratioH >= ratioW) {
    cropRect.size.width = floor (size.width / ratioH);
    cropRect.size.height = sourceSize.height;
} else {
    cropRect.size.width = sourceSize.width;
    cropRect.size.height = floor(size.height / ratioW);
}

cropRect.origin.x = floor( (sourceSize.width - cropRect.size.width)/2 );
cropRect.origin.y = floor( (sourceSize.height - cropRect.size.height)/2 );



[targetImage lockFocus];

[sourceImage drawInRect:targetFrame
               fromRect:cropRect       //portion of source image to draw
              operation:NSCompositeCopy  //compositing operation
               fraction:1.0              //alpha (transparency) value
         respectFlipped:YES              //coordinate system
                  hints:@{NSImageHintInterpolation:
 [NSNumber numberWithInt:NSImageInterpolationLow]}];

[targetImage unlockFocus];

return targetImage;}
Shagru
  • 161
  • 1
  • 5
  • 1
    If you'd like one with some aspect ratio options: [here you go](https://github.com/spilliams/NSToolkit/blob/master/NSImage%2BResize.m). – Spencer Williams Dec 23 '14 at 23:48
4

Here's an awesome category for NSImage: NSImage+ContentMode

It allows content modes like in iOS, works great.

Avishay Cohen
  • 2,418
  • 1
  • 22
  • 40
  • Excellent. Note: you need to replace `UIGraphicsGetCurrentContext()` with `[NSGraphicsContext currentContext].CGContext` for MacOS. Especially useful after High Sierra messed up the NSImageView content during CABasicAnimation – codrut Oct 05 '17 at 11:11
  • AspectFit did not work correctly for me when h>w. This is what worked: `if (contentMode == UIViewContentModeScaleAspectFit) { CGSize imageSize = CGSizeMake(self.size.width, self.size.height); CGSize containerSize = rect.size; CGFloat widthRaport = containerSize.width/imageSize.width; CGFloat heightRaport = containerSize.height/imageSize.height; CGFloat scale = MIN(widthRaport, heightRaport); imageSize.width = imageSize.width * scale; imageSize.height = imageSize.height * scale;` – codrut Oct 25 '17 at 11:08
2

Set image scaling property to NSImageScaleAxesIndependently which will scale image to fill rectangle.This will not preserve aspect ratio.

Ganesh Amrule
  • 407
  • 2
  • 12
1

Swift version of @Shagru's answer (without the hints)

func resizeImage(_ sourceImage:NSImage, size:CGSize) -> NSImage
    {
        let targetFrame = CGRect(origin: CGPoint.zero, size: size);
        let targetImage = NSImage.init(size: size)
        let sourceSize = sourceImage.size
        let ratioH = size.height / sourceSize.height;
        let ratioW = size.width / sourceSize.width;

        var cropRect = CGRect.zero;
        if (ratioH >= ratioW) {
            cropRect.size.width = floor (size.width / ratioH);
            cropRect.size.height = sourceSize.height;
        } else {
            cropRect.size.width = sourceSize.width;
            cropRect.size.height = floor(size.height / ratioW);
        }

        cropRect.origin.x = floor( (sourceSize.width - cropRect.size.width)/2 );
        cropRect.origin.y = floor( (sourceSize.height - cropRect.size.height)/2 );
        targetImage.lockFocus()
        sourceImage.draw(in: targetFrame, from: cropRect, operation: .copy, fraction: 1.0, respectFlipped: true, hints: nil )


        targetImage.unlockFocus()
        return targetImage;
    }
jvcleave
  • 91
  • 2
  • 5