【发布时间】:2021-09-08 19:24:49
【问题描述】:
所以我习惯了UIImageView,并且能够设置其图像在其中显示的不同方式。比如AspectFill模式等...
我想在 Mac 应用程序上使用 NSImageView 来完成同样的事情。 NSImageView 在这方面与UIImageView 的工作方式相似吗?或者我将如何在NSImageView 中显示图像并选择不同的显示方式?
【问题讨论】:
标签: macos nsimageview
所以我习惯了UIImageView,并且能够设置其图像在其中显示的不同方式。比如AspectFill模式等...
我想在 Mac 应用程序上使用 NSImageView 来完成同样的事情。 NSImageView 在这方面与UIImageView 的工作方式相似吗?或者我将如何在NSImageView 中显示图像并选择不同的显示方式?
【问题讨论】:
标签: macos nsimageview
您可能会发现继承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;
}
注意先设置层,再设置wantsLayer的顺序非常重要(如果你先设置wantsLayer,你会得到一个默认的backing layer)。
您可以有一个简单地更新图层内容的setImage 方法。
【讨论】:
这是我正在使用的,用 Swift 编写的。这种方法适用于情节提要 - 只需使用普通的 NSImageView,然后将 Class 框中的名称 NSImageView 替换为 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
}
}
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
//the image setter isn't called when loading from a storyboard
//manually set the image if it is already set
required public init?(coder: NSCoder) {
super.init(coder: coder)
if let theImage = image {
self.image = theImage
}
}
}
【讨论】:
我遇到了同样的问题。我想让图像被缩放以填充但保持原始图像的纵横比。奇怪的是,这并不像看起来那么简单,并且不是开箱即用的 NSImageView。我希望 NSImageView 在使用超级视图调整大小时很好地缩放。我做了一个插入式 NSImageView 子类,你可以在 github 上找到:KPCScaleToFillNSImageView
【讨论】:
您可以使用这个:图像将被强制填充视图大小
(方面填充)
imageView.imageScaling = .scaleAxesIndependently
(宽高比)
imageView.imageScaling = .scaleProportionallyUpOrDown
(中上)
imageView.imageScaling = .scaleProportionallyDown
它对我有用。
【讨论】:
我很难弄清楚如何制作Aspect Fill Clip to Bounds:
图片来源: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,所以你可以在故事板中设置它
警告
我是 MacOS Swift 开发的新手,我来自 iOS 开发,这就是为什么我很惊讶我找不到 clipToBound 属性,也许它存在但我找不到找到它!
关于代码,我怀疑这会消耗很多,而且随着时间的推移会产生修改原始图像比例的副作用。这种副作用对我来说似乎可以忽略不计。
如果他们的设置允许NSImageView 剪辑到边界,请删除此答案:]
【讨论】:
view.layer.masksToBounds = true
图片缩放可以用NSImageView下面的函数更新。
[imageView setImageScaling:NSScaleProportionally];
这里有更多更改图像显示属性的选项。
enum {
NSScaleProportionally = 0, // Deprecated. Use NSImageScaleProportionallyDown
NSScaleToFit, // Deprecated. Use NSImageScaleAxesIndependently
NSScaleNone // Deprecated. Use NSImageScaleNone
};
【讨论】:
这是另一种在后台使用 SwiftUI 的方法
这里的主要优点是,如果您的图像具有明暗模式,那么当系统外观发生变化时它们会受到尊重 (我无法让它与其他方法一起使用)
这依赖于您的资产中存在的带有 imageName 的图像
import Foundation
import AppKit
import SwiftUI
open class AspectFillImageView : NSView {
@IBInspectable
open var imageName: String?
{
didSet {
if imageName != oldValue {
insertSwiftUIImage(imageName)
}
}
}
open override func prepareForInterfaceBuilder() {
self.needsLayout = true
}
func insertSwiftUIImage(_ name:String?){
self.removeSubviews()
guard let name = name else {
return
}
let iv = Image(name).resizable().scaledToFill()
let hostView = NSHostingView(rootView:iv)
self.addSubview(hostView)
//I'm using PureLayout to pin the subview. You will have to rewrite this in your own way...
hostView.autoPinEdgesToSuperviewEdges()
}
func commonInit() {
insertSwiftUIImage(imageName)
}
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
commonInit()
}
required public init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
}
【讨论】: