NSImageView image aspect fill?

2020-05-23 03:01发布

问题:

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?

回答1:

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.



回答2:

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



回答3:

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
    }
  }
}


回答4:

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.



回答5:

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 :]



回答6:

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
};