So I am used to UIImageView
, and being able to set different ways of how its image is displayed in it. Like for example AspectFill
mode etc...
I would like to accomplish the same thing using NSImageView
on a mac app. Does NSImageView
work similarly to UIImageView
in that regard or how would I go about showing an image in an NSImageView
and picking different ways of displaying that image?
You may find it much easier to subclass NSView
and provide a CALayer
that does the aspect fill for you. Here is what the init
might look like for this NSView
subclass.
- (id)initWithFrame:(NSRect)frame andImage:(NSImage*)image
{
self = [super initWithFrame:frame];
if (self) {
self.layer = [[CALayer alloc] init];
self.layer.contentsGravity = kCAGravityResizeAspectFill;
self.layer.contents = image;
self.wantsLayer = YES;
}
return self;
}
Note that the order of setting the layer, then settings wantsLayer is very important (if you set wantsLayer first, you'll get a default backing layer instead).
You could have a setImage
method that simply updates the contents of the layer.
I had the same problem. I wanted to have the image to be scaled to fill but keeping the aspect ratio of the original image. Strangely, this is not as simple as it seems, and does not come out of the box with NSImageView. I wanted the NSImageView scale nicely while it resize with superview(s). I made a drop-in NSImageView subclass you can find on github: KPCScaleToFillNSImageView
Here is what I'm using, written with Swift. This approach works well with storyboards - just use a normal NSImageView, then replace the name NSImageView in the Class box, with MyAspectFillImageNSImageView ...
open class MyAspectFillImageNSImageView : NSImageView {
open override var image: NSImage? {
set {
self.layer = CALayer()
self.layer?.contentsGravity = kCAGravityResizeAspectFill
self.layer?.contents = newValue
self.wantsLayer = true
super.image = newValue
}
get {
return super.image
}
}
}
You can use this: image will be force to fill the view size
( Aspect Fill )
imageView.imageScaling = .scaleAxesIndependently
( Aspect Fit )
imageView.imageScaling = .scaleProportionallyUpOrDown
( Center Top )
imageView.imageScaling = .scaleProportionallyDown
It works for me.
I was having an hard time trying to figure out how you can make an Aspect Fill Clip to Bounds :
Picture credit: https://osxentwicklerforum.de/index.php/Thread/28812-NSImageView-Scaling-Seitenverh%C3%A4ltnis/
Finally I made my own Subclass of NSImageView, hope this can help someone :
import Cocoa
@IBDesignable
class NSImageView_ScaleAspectFill: NSImageView {
@IBInspectable
var scaleAspectFill : Bool = false
override func awakeFromNib() {
// Scaling : .scaleNone mandatory
if scaleAspectFill { self.imageScaling = .scaleNone }
}
override func draw(_ dirtyRect: NSRect) {
if scaleAspectFill, let _ = self.image {
// Compute new Size
let imageViewRatio = self.image!.size.height / self.image!.size.width
let nestedImageRatio = self.bounds.size.height / self.bounds.size.width
var newWidth = self.image!.size.width
var newHeight = self.image!.size.height
if imageViewRatio > nestedImageRatio {
newWidth = self.bounds.size.width
newHeight = self.bounds.size.width * imageViewRatio
} else {
newWidth = self.bounds.size.height / imageViewRatio
newHeight = self.bounds.size.height
}
self.image!.size.width = newWidth
self.image!.size.height = newHeight
}
// Draw AFTER resizing
super.draw(dirtyRect)
}
}
Plus this is @IBDesignable
so you can set it on in the StoryBoard
WARNINGS
I'm new to MacOS Swift development, I come from iOS development that's why I was surprised I couldn't find a clipToBound property, maybe it exists and I wasn't able to find it !
Regarding the code, I suspect this is consuming a lot, and also this has the side effect to modify the original image ratio over the time. This side effect seemed negligible to me.
Once again if their is a setting that allow a NSImageView
to clip to bounds, please remove this answer :]
For getting AspectFit
property of UIImageView
in Cocoa NSImageView
have this method
[imageView setImageScaling:NSScaleProportionally];
Here are more options to change image display property.
enum {
NSScaleProportionally = 0, // Deprecated. Use NSImageScaleProportionallyDown
NSScaleToFit, // Deprecated. Use NSImageScaleAxesIndependently
NSScaleNone // Deprecated. Use NSImageScaleNone
};