NSImageView图像方面填充?

时间:2014-04-11 02:52:06

标签: macos nsimageview

所以我习惯UIImageView,能够设置不同的图像显示方式。例如AspectFill模式等...

我想在mac app上使用NSImageView完成同样的事情。 NSImageView在这方面是否与UIImageView的工作方式类似,或者如何在NSImageView中展示图像并选择不同的方式来显示该图像?

7 个答案:

答案 0 :(得分:22)

您可能会发现更轻松地继承NSView并提供为您填充方面的CALayer。以下是此init子类的NSView可能的样子。

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

请注意,设置图层的顺序,然后设置wantLayer 非常重要(如果先设置wantsLayer,则会获得默认的支持图层。)

您可以使用setImage方法只更新图层的内容。

答案 1 :(得分:13)

我遇到了同样的问题。我希望缩放图像以填充,但保持原始图像的纵横比。奇怪的是,这并不像看起来那么简单,并且没有开箱即用的NSImageView。我希望NSImageView在使用superview(s)调整大小时能够很好地扩展。我创建了一个可以在github上找到的插入式NSImageView子类:KPCScaleToFillNSImageView

答案 2 :(得分:5)

你可以使用这个:图像将强制填充视图尺寸

(Aspect Fill)

imageView.imageScaling = .scaleAxesIndependently


(Aspect Fit)

imageView.imageScaling = .scaleProportionallyUpOrDown


(中心顶部)

imageView.imageScaling = .scaleProportionallyDown

它对我有用。

答案 3 :(得分:4)

这是我正在使用的,用Swift编写的。这种方法适用于故事板 - 只需使用普通的NSImageView,然后使用MyAspectFillImageNSImageView替换类框中的名称NSImageView ......

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 :(得分:1)

在Cocoa AspectFit中获取UIImageView的{​​{1}}属性有此方法

NSImageView

以下是更改图像显示属性的更多选项。

[imageView setImageScaling:NSScaleProportionally];

答案 5 :(得分:0)

我很难弄清楚如何制作 Aspect填充剪辑到边界

Credit : https://osxentwicklerforum.de/index.php/Thread/28812-NSImageView-Scaling-Seitenverh%C3%A4ltnis/ 图片来源:https://osxentwicklerforum.de/index.php/Thread/28812-NSImageView-Scaling-Seitenverh%C3%A4ltnis/

最后,我制作了自己的NSImageView子类,希望这可以对某人有所帮助:

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

加上这是@IBDesignable,因此您可以在StoryBoard上将其设置为

警告

  • 我是MacOS Swift开发的新手,我来自iOS开发,这就是为什么我很惊讶地找不到 clipToBound 属性的原因,也许它存在并且我无法找到它!

  • 关于代码,我怀疑这会消耗很多,而且还会随着时间的推移而影响修改原始图像比例的副作用。对我来说,这种副作用微不足道。

如果它们的设置再次允许NSImageView限制在范围内,请再次删除此答案:]

答案 6 :(得分:0)

使用CALayer显示图像意味着它不会响应暗模式的变化。 我无法更改CALayer的内容,因此我使用手动调整大小的NSView构建了新版本。

注意-尽管您可以添加此视图并在InterfaceBuilder中设置图像,但它不会在那里显示。我无法使用@IBDesignable。

import Foundation
import AppKit

open class AspectFillNSImageView : NSView {

    @IBInspectable
    open var image: NSImage? {
        didSet {
            subImageView.image = image
        }
    }

    open override func prepareForInterfaceBuilder() {
        self.needsLayout = true
    }


    
    open override func layout() {
        super.layout()

        guard let subImage = subImageView.image else {
            subImageView.frame = bounds
            return
        }

        let xScale = subImage.size.width / self.bounds.size.width
        let yScale = subImage.size.width / self.bounds.size.height

        let aspectFitScale = min(xScale,yScale)
        let newSize = CGSize.init(x: subImage.size.width / aspectFitScale,
                                  y: subImage.size.height / aspectFitScale)
        let offset = CGPoint.init(x: (bounds.width - newSize.width)/2,
                                  y: (bounds.height - newSize.height)/2)

        subImageView.frame = CGRect.init(origin: offset, size: newSize)
    }
    
    var subImageView = NSImageView.init(frame: .zero)
    func commonInit() {
        self.translatesAutoresizingMaskIntoConstraints = false
        subImageView.imageScaling = .scaleProportionallyUpOrDown
        self.addSubview(subImageView)

        
        //observe frame changes
        self.postsFrameChangedNotifications = true
        NotificationCenter.default.addObserver(forName: NSView.frameDidChangeNotification,
                                               object: self, queue: nil) { [weak self] (_) in
                                                self?.needsLayout = true
        }
        
        if let anImage = self.image {
            self.image = anImage
            //doLayout()
        }
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    public override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        
        commonInit()
    }


    required public init?(coder: NSCoder) {
        super.init(coder: coder)

        commonInit()
    }
}