所以我习惯UIImageView
,能够设置不同的图像显示方式。例如AspectFill
模式等...
我想在mac app上使用NSImageView
完成同样的事情。 NSImageView
在这方面是否与UIImageView
的工作方式类似,或者如何在NSImageView
中展示图像并选择不同的方式来显示该图像?
答案 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填充剪辑到边界:
图片来源: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()
}
}