I'm writing a QuickLook extension for my app, and I want to be able to display the actual icon image for a particular file, as shown in Finder. I've tried using QLThumbnailGenerator for this, but it will always return a plain white document icon.
QLThumbnailGenerationRequest *request = [[QLThumbnailGenerationRequest alloc] initWithFileAtURL:url size:size scale:scale representationTypes:QLThumbnailGenerationRequestRepresentationTypeIcon];
QLThumbnailGenerator *generator = [QLThumbnailGenerator sharedGenerator];
__unsafe_unretained PreviewViewController *weakSelf = self;
[generator generateBestRepresentationForRequest:request completionHandler:^(QLThumbnailRepresentation *thumbnail, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (thumbnail == nil || error) {
NSLog(@"Failed to generate thumbnail! %@", error);
// Handle the error case gracefully
} else {
// Display the thumbnail that you created
NSLog(@"Generated thumbnail!");
weakSelf.imageView.image = thumbnail.NSImage;
}
});
}];
Original code here.
import SwiftUI
import QuickLookThumbnailing
struct ThumbnailImageView: View {
let url: URL
let size: CGFloat
@State private var thumbnail: NSImage? = nil
var body: some View {
Group {
if let thumbnail = thumbnail {
Image(nsImage: thumbnail)
} else {
Image(systemName: "photo") // << any placeholder
.onAppear(perform: generateThumbnail) // << here !!
}
}
}
func generateThumbnail() {
let size: CGSize = CGSize(width: size, height: size)
let request = QLThumbnailGenerator.Request(fileAt: url, size: size, scale: 1.0, representationTypes: .lowQualityThumbnail)
request.iconMode = true
let generator = QLThumbnailGenerator.shared
generator.generateRepresentations(for: request) { (thumbnail, type, error) in
DispatchQueue.main.async {
if thumbnail == nil || error != nil {
assert(false, "Thumbnail failed to generate")
} else {
DispatchQueue.main.async { // << required !!
self.thumbnail = thumbnail!.nsImage // here !!
}
}
}
}
}
}
usage:
ThumbnailImageView(url: url, size: 100)
and another way:
import Foundation
import SwiftUI
import Quartz
import QuickLook
struct UKSImage: View {
let url: URL
let size: CGFloat
@State private var thumbnail: NSImage? = nil
var body: some View {
if let thumbnail = thumbnail {
Image(nsImage: thumbnail)
.resizable()
.scaledToFit()
} else {
Image(systemName: "photo") // << any placeholder
.onAppear(perform: generateThumbnail) // << here !!
}
}
func generateThumbnail() {
DispatchQueue.global(qos: .background).async {
self.thumbnail = url.imgThumbnailAdv(size)
}
}
}
fileprivate extension URL {
func imgThumbnailAdv(_ size: CGFloat) -> NSImage? {
let extensionsExceptions: [String] = ["txt","docx","doc","pages","odt","rtf","tex","wpd","ltxd",
"btxt","dotx","wtt","dsc","me","ans","log","xy","text","docm",
"wps","rst","readme","asc","strings","docz","docxml","sdoc",
"plain","notes","latex","utxt","ascii",
"xlsx","patch","xls","xlsm","ods",
"py","cs","swift","html","css", "fountain","gscript","lua",
"markdown","md",
"plist"
]
if extensionsExceptions.contains(self.pathExtension.lowercased()) {
let img = NSWorkspace.shared.highResIcon(forPath: self.path, resolution: Int(size))
return img
}
if let img2 = self.getImgThumbnail(size) {
return img2
}
return NSWorkspace.shared.highResIcon(forPath: self.path, resolution: Int(size))
}
}
extension NSImage{
var pixelSize: NSSize?{
if let rep = self.representations.first{
let size = NSSize(width: rep.pixelsWide, height: rep.pixelsHigh)
return size
}
return nil
}
}
fileprivate extension URL {
func getImgThumbnail(_ size: CGFloat) -> NSImage? {
let ref = QLThumbnailCreate ( kCFAllocatorDefault,
self as NSURL,
CGSize(width: size, height: size),
[ kQLThumbnailOptionIconModeKey: false ] as CFDictionary
)
guard let thumbnail = ref?.takeRetainedValue()
else { return nil }
if let cgImageRef = QLThumbnailCopyImage(thumbnail) {
let cgImage = cgImageRef.takeRetainedValue()
return NSImage(cgImage: cgImage, size: CGSize(width: cgImage.width, height: cgImage.height))
}
return nil
}
}
extension NSWorkspace {
func highResIcon(forPath path: String, resolution: Int = 512) -> NSImage? {
if let rep = self.icon(forFile: path)
.bestRepresentation(for: NSRect(x: 0, y: 0, width: resolution, height: resolution), context: nil, hints: nil) {
let image = NSImage(size: rep.size)
image.addRepresentation(rep)
return image
}
return nil
}
}
usage:
UKSImage(url: url, size: 100)