So, tried creating two separate queues in ViewController.swift for improving the speed of loading and viewing images, but it doesn’t seem to be any faster. Any suggestions?
Here’s the code (Swift 4.2):
import Cocoa
class ViewController: NSViewController
{
let processingQueue: OperationQueue =
{
let result = OperationQueue()
// result.maxConcurrentOperationCount = 4 // no limit on processingQueue.
return result
}()
let ioQueue: OperationQueue =
{
let result = OperationQueue()
result.maxConcurrentOperationCount = 4 // limit since io reading can be slow.
return result
}()
var textLayer: CATextLayer!
var text: String?
{
didSet
{
let font = NSFont.systemFont(ofSize: textLayer.fontSize)
let attributes = [NSAttributedString.Key.font : font]
var size = text?.size(withAttributes: attributes) ?? CGSize.zero
// Ensure that the size is in whole numbers:
size.width = ceil(size.width)
size.height = ceil(size.height)
textLayer.bounds = CGRect(origin: CGPoint.zero, size: size)
textLayer.superlayer!.bounds = CGRect(x: 0, y: 0, width: size.width + 16, height: size.height + 20)
textLayer.string = text
}
}
override func viewDidLoad()
{
super.viewDidLoad()
// Set view to be layer-hosting:
view.layer = CALayer()
view.wantsLayer = true
let textContainer = CALayer()
textContainer.anchorPoint = CGPoint.zero
textContainer.position = CGPoint(x: 10, y: 10)
textContainer.zPosition = 100
textContainer.backgroundColor = NSColor.black.cgColor
textContainer.borderColor = NSColor.white.cgColor
textContainer.borderWidth = 2
textContainer.cornerRadius = 15
textContainer.shadowOpacity = 0.5
view.layer!.addSublayer(textContainer)
let textLayer = CATextLayer()
textLayer.anchorPoint = CGPoint.zero
textLayer.position = CGPoint(x: 10, y: 6)
textLayer.zPosition = 100
textLayer.fontSize = 24
textLayer.foregroundColor = NSColor.white.cgColor
self.textLayer = textLayer
textContainer.addSublayer(textLayer)
// Relay on text's didSet to update textLayer's bounds:
text = "Loading..."
let url = URL(fileURLWithPath: "/Library/Desktop Pictures", isDirectory: true)
addImagesFromFolderURL(url)
}
func thumbImageFromImage(_ image: NSImage) -> NSImage
{
let targetHeight: CGFloat = 200.0
let imageSize = image.size
let smallerSize = NSSize(width: targetHeight + imageSize.width / imageSize.height,
height: targetHeight)
let smallerImage = NSImage(size: smallerSize,
flipped: false,
drawingHandler: { (rect) -> Bool in
image.draw(in: rect)
return true
})
return smallerImage
}
func addImagesFromFolderURL(_ folderURL: URL)
{
let t0 = NSDate.timeIntervalSinceReferenceDate
ioQueue.addOperation
{
let fileManager = FileManager()
let directoryEnumerator = fileManager.__enumerator(at: folderURL,
includingPropertiesForKeys: nil,
options: [],
errorHandler: nil)!
while let url = directoryEnumerator.nextObject() as? NSURL
{
// Skip directories:
var isDirectoryValue: AnyObject?
do
{
try url.getResourceValue(&isDirectoryValue,
forKey: URLResourceKey.isDirectoryKey)
}
catch
{
print("error checking whether URL is directory: \(error)")
continue
}
self.ioQueue.addOperation
{
guard let isDirectory = isDirectoryValue as? Bool, isDirectory == false
else { return }
guard let image = NSImage(contentsOf: url as URL)
else { return }
self.processingQueue.addOperation
{
let thumbImage = self.thumbImageFromImage(image)
OperationQueue.main.addOperation
{
self.presentImage(thumbImage)
let t1 = NSDate.timeIntervalSinceReferenceDate
let interval = t1 - t0
self.text = String(format: "%0.1fs", interval)
}
}
}
}
}
}
func presentImage(_ image: NSImage)
{
let superlayerBounds = view.layer!.bounds
let center = CGPoint(x: superlayerBounds.midX, y: superlayerBounds.midY)
let imageBounds = CGRect(origin: CGPoint.zero, size: image.size)
let randomPoint =
CGPoint(x: CGFloat(arc4random_uniform(UInt32(superlayerBounds.maxX))),
y: CGFloat(arc4random_uniform(UInt32(superlayerBounds.maxY))))
let timingFunction
= CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
let positionAnimation = CABasicAnimation()
positionAnimation.fromValue = NSValue(point: center)
positionAnimation.duration = 1.5
positionAnimation.timingFunction = timingFunction
let boundsAnimation = CABasicAnimation()
boundsAnimation.fromValue = NSValue(rect: CGRect.zero)
boundsAnimation.duration = 1.5
boundsAnimation.timingFunction = timingFunction
let layer = CALayer()
layer.contents = image
layer.actions =
["position" : positionAnimation,
"bounds" : boundsAnimation]
CATransaction.begin()
view.layer!.addSublayer(layer)
layer.position = randomPoint
layer.bounds = imageBounds
CATransaction.commit()
}
}