Swift 在本地以不同格式保存NSImage

Swift 在本地以不同格式保存NSImage,swift,macos,Swift,Macos,我需要允许用户将NSImage保存到本地文件 因此,我使用这些扩展,以便可以保存在PNG格式的图像 extension NSBitmapImageRep { var png: Data? { return representation(using: .png, properties: [:]) } } extension Data { var bitmap: NSBitmapImageRep? { return NSBitmapImageR

我需要允许用户将
NSImage
保存到本地文件

因此,我使用这些扩展,以便可以保存在PNG格式的图像

extension NSBitmapImageRep {
    var png: Data? {
        return representation(using: .png, properties: [:])
    }
}
extension Data {
    var bitmap: NSBitmapImageRep? {
        return NSBitmapImageRep(data: self)
    }
}
extension NSImage {
    var png: Data? {
        return tiffRepresentation?.bitmap?.png

    }
    func savePNG(to url: URL) -> Bool {
        do {
            try png?.write(to: url)
            return true
        } catch {
            print(error)
            return false
        }

    }
}

有没有一种更简单的方法来保存不同格式的
NSImage
,如JPEG、TIFF、BMP等你可以通过将你的
NSImage
转换成
NSBitmapImageRep
然后使用它的
表示(使用:)
方法来获得BMP、GIF、JPEG、JPEG2000、PNG或TIFF,但这有点麻烦;您必须创建一个空的
NSBitmapImageRep
,将其设置为当前图形上下文,然后在其中绘制
NSImage
(有关详细信息,请参阅)

如果您需要macOS 10.13,在我看来,
CIContext
上有一些简便的方法更易于使用,并且可以将图像转换为TIFF、JPEG或PNG(但没有BMP、GIF或JPEG2000;此外,还有一种转换为HEIF的方法,在撰写本文时似乎只有iOS):


您可以通过将
NSImage
转换为
NSBitmapImageRep
并使用其
表示(使用:)
方法来获取BMP、GIF、JPEG、JPEG2000、PNG或TIFF,但这有点麻烦;您必须创建一个空的
NSBitmapImageRep
,将其设置为当前图形上下文,然后在其中绘制
NSImage
(有关详细信息,请参阅)

如果您需要macOS 10.13,在我看来,
CIContext
上有一些简便的方法更易于使用,并且可以将图像转换为TIFF、JPEG或PNG(但没有BMP、GIF或JPEG2000;此外,还有一种转换为HEIF的方法,在撰写本文时似乎只有iOS):


您可以创建一个自定义方法,允许您指定任何图像类型以及要保存NSImage的目录。您还可以将目标目录的默认值设置为当前目录,因此如果不传递目录url,它将保存到当前目录:

extension NSImage {
    func save(as fileName: String, fileType: NSBitmapImageRep.FileType = .jpeg, at directory: URL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)) -> Bool {
        guard let tiffRepresentation = tiffRepresentation, directory.isDirectory, !fileName.isEmpty else { return false }
        do {
            try NSBitmapImageRep(data: tiffRepresentation)?
                .representation(using: fileType, properties: [:])?
                .write(to: directory.appendingPathComponent(fileName).appendingPathExtension(fileType.pathExtension))
            return true
        } catch {
            print(error)
            return false
        }
    }
}

您还需要确保传递给方法的url是目录url。您可以使用URL resourceValues方法获取URL isDirectoryKey值并检查其是否为真:

extension URL {
    var isDirectory: Bool {
       return (try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
    }
}

您还可以扩展NSBitmapImageRep.FileType以提供关联的文件路径扩展名:

extension NSBitmapImageRep.FileType {
    var pathExtension: String {
        switch self {
        case .bmp:
            return "bmp"
        case .gif:
            return "gif"
        case .jpeg:
            return "jpg"
        case .jpeg2000:
            return "jp2"
        case .png:
            return "png"
        case .tiff:
            return "tif"
        }
    }
}

操场测试:

let desktopDirectory = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first!
// lets change the current directory to the desktop directory
FileManager.default.changeCurrentDirectoryPath(desktopDirectory.path)

// get your nsimage
let picture  = NSImage(contentsOf: URL(string: "https://i.stack.imgur.com/Xs4RX.jpg")!)!

// this will save to the current directory
if picture.save(as: "profile") {
    print("file saved as profile.jpg which is the default type")
}
if picture.save(as: "profile", fileType: .png) {
    print("file saved as profile.png")
}
if picture.save(as: "profile", fileType: .tiff) {
    print("file saved as profile.tif")
}
if picture.save(as: "profile", fileType: .gif) {
    print("file saved as profile.gif")
}
if picture.save(as: "profile", fileType: .jpeg2000) {
    print("file saved as profile.jp2")
}

// you can also chose a choose another directory without the need to change the current directory
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
if picture.save(as: "profile", at: url) {
    print("file saved as profile.jpg at documentDirectory")
}

您可以创建一个自定义方法,允许您指定任何图像类型以及要保存NSImage的目录。您还可以将目标目录的默认值设置为当前目录,因此如果不传递目录url,它将保存到当前目录:

extension NSImage {
    func save(as fileName: String, fileType: NSBitmapImageRep.FileType = .jpeg, at directory: URL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)) -> Bool {
        guard let tiffRepresentation = tiffRepresentation, directory.isDirectory, !fileName.isEmpty else { return false }
        do {
            try NSBitmapImageRep(data: tiffRepresentation)?
                .representation(using: fileType, properties: [:])?
                .write(to: directory.appendingPathComponent(fileName).appendingPathExtension(fileType.pathExtension))
            return true
        } catch {
            print(error)
            return false
        }
    }
}

您还需要确保传递给方法的url是目录url。您可以使用URL resourceValues方法获取URL isDirectoryKey值并检查其是否为真:

extension URL {
    var isDirectory: Bool {
       return (try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
    }
}

您还可以扩展NSBitmapImageRep.FileType以提供关联的文件路径扩展名:

extension NSBitmapImageRep.FileType {
    var pathExtension: String {
        switch self {
        case .bmp:
            return "bmp"
        case .gif:
            return "gif"
        case .jpeg:
            return "jpg"
        case .jpeg2000:
            return "jp2"
        case .png:
            return "png"
        case .tiff:
            return "tif"
        }
    }
}

操场测试:

let desktopDirectory = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first!
// lets change the current directory to the desktop directory
FileManager.default.changeCurrentDirectoryPath(desktopDirectory.path)

// get your nsimage
let picture  = NSImage(contentsOf: URL(string: "https://i.stack.imgur.com/Xs4RX.jpg")!)!

// this will save to the current directory
if picture.save(as: "profile") {
    print("file saved as profile.jpg which is the default type")
}
if picture.save(as: "profile", fileType: .png) {
    print("file saved as profile.png")
}
if picture.save(as: "profile", fileType: .tiff) {
    print("file saved as profile.tif")
}
if picture.save(as: "profile", fileType: .gif) {
    print("file saved as profile.gif")
}
if picture.save(as: "profile", fileType: .jpeg2000) {
    print("file saved as profile.jp2")
}

// you can also chose a choose another directory without the need to change the current directory
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
if picture.save(as: "profile", at: url) {
    print("file saved as profile.jpg at documentDirectory")
}


func保存(到url:url,格式为:ImageFormat=.PNG)
其中
ImageFormat
是您定义的枚举吗?尽管如此,我还是主张只扩展
数据
以包含
保存
函数,这样您的代码就是
image.png?.save()
image.tiff?.save()
,等等@nhgrif谢谢。。但我对斯威夫特还不熟悉。。。。作为回答,您能更好地解释一下吗。
func save(到url:url,作为格式:ImageFormat=.PNG)
其中
ImageFormat
是您定义的枚举吗?尽管如此,我还是主张只扩展
数据
以包含
保存
函数,这样您的代码就是
image.png?.save()
image.tiff?.save()
,等等@nhgrif谢谢。。但我对斯威夫特还不熟悉。。。。作为回答,你能更好地解释一下吗?听起来不对。“易于使用”?@eltoma将上面的代码片段与链接到的
NSBitmapImageRep
版本进行比较。@CharlesSrstka我使用的是Xcode版本8.3.3,运行在OSX Sierra上。我是OSX开发新手。我不知道我使用的swift的确切版本,也不知道使用此版本的Xcode开发的应用程序是否与旧版本兼容我更喜欢一种兼容性更好的方法。。但是,在swift中,似乎有很多东西可以简单地保存图像。在C中,我们使用
bitmap.save()
@techno,那么您可能应该使用我链接到的基于
NSBitmapImageRep
的解决方案。它与您可能遇到的每个版本的macOS/OS X都兼容。CIContext?听起来不对。“易于使用”?@eltoma将上面的代码片段与链接到的
NSBitmapImageRep
版本进行比较。@CharlesSrstka我使用的是Xcode版本8.3.3,运行在OSX Sierra上。我是OSX开发新手。我不知道我使用的swift的确切版本,也不知道使用此版本的Xcode开发的应用程序是否与旧版本兼容我更喜欢一种兼容性更好的方法。。但是,在swift中,似乎有很多东西可以简单地保存图像。在C中,我们使用
bitmap.save()
@techno,那么您可能应该使用我链接到的基于
NSBitmapImageRep
的解决方案。它与您可能遇到的每个版本的macOS/OS X都兼容。谢谢。。但是我已经写了一个函数来实现同样的功能。你能过来聊天吗。。我可以展示我写的代码。你能检查一下吗。@techno发布链接谢谢。。但是我已经写了一个函数来实现同样的功能。你能过来聊天吗。。我可以显示我写的代码。你能检查一下吗。@techno发布链接