I've found I can fix this by disabling image interpolation in the graphics context. This override will disable interpolation as long as the image is not larger than the draw rect, so it will look crisp if it fits in the view but retains the interpolation if it needs to scale down.
public class CustomImageView: NSImageView {
public override func draw(_ dirtyRect: NSRect) {
if let image = self.image, image.size.width <= dirtyRect.size.width && image.size.height <= dirtyRect.size.height {
NSGraphicsContext.current?.cgContext.interpolationQuality = .none
}
super.draw(dirtyRect)
NSGraphicsContext.current?.cgContext.interpolationQuality = .default
}
}
Still interested in any other answers or info about this. I'm surprised I can't find anything when searching, seems like it would be a common issue to me.
UPDATE
Setting the interpolation directly on the NSGraphicsContext isn't working on macOS 11 (possibly a bug in the system?). It only works if you target the cgContext.