NSRect.fill() not working with clear colors on Xcode 9 Swift 4

2.6k views Asked by At

Swift 4 has a new NSRect.fill() function to replace NSRectFill(). When attempting to clear a bitmap using NSColor.clear.setFill(), the bitmap remains unchanged using NSRect.fill().

A workaround is to use NSRect.fill(using:) and specifying .copy.

Here is what the code does in Playgrounds in Swift 4: Swift 4

Sample code:

// Duplicating a bug with Xcode 9 and Swift 4

import Cocoa

var image = NSImage(size: NSSize(width: 32, height: 32))
let rect = NSRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
image.lockFocus()
NSColor.red.setFill()
rect.fill()
image.unlockFocus()
image.lockFocus()
NSColor.clear.setFill()
// calling fill() with a clear color does not work
rect.fill()
image.unlockFocus()
// Image above should not be red
image.lockFocus()
NSColor.clear.setFill()
// using fill(using:) does work
rect.fill(using: .copy)
image.unlockFocus()
// image above is properly cleared

Is this a bug that I should file with Apple or am I missing something? This sequence worked in Swift 3 using NSRectFill().

Here is Swift 3 using NSRectFill: Swift 3

1

There are 1 answers

0
Cœur On

They have simply different default logic:
Old syntax was always .copy.
New syntax is context if present or .sourceOver.

void NSRectFill(NSRect rect);

Fills aRect with the current color using the compositing mode NSCompositingOperationCopy

(source: https://developer.apple.com/documentation/appkit/1473652-nsrectfill)

/// Fills this rect in the current NSGraphicsContext in the context's fill
/// color.
/// The compositing operation of the fill defaults to the context's
/// compositing operation, not necessarily using `.copy` like `NSRectFill()`.
/// - precondition: There must be a set current NSGraphicsContext.
@available(swift 4)
public func fill(using operation: NSCompositingOperation = NSGraphicsContext.current?.compositingOperation ?? .sourceOver)

(source: Xcode comments for https://developer.apple.com/documentation/corefoundation/cgrect/2903486-fill/)

So if you're converting old code to newer format, then rect.fill(using: .copy) is the syntax to keep identical behaviour.