Tuesday, December 22, 2020

Undocumented NSShadow Change on Catalina

Jeff Johnson (tweet):

Curiously, the API documentation for shadowOffset is now shared between AppKit and UIKit.

[…]

So there is a change in behavior of NSShadow shadowOffset in AppKit on Catalina, presumably to emulate UIKit. Sigh… why does the Mac always have to emulate iOS now? Unfortunately, AppKit developers were not informed of the change, because there were no AppKit release notes published for Catalina.

If you want the shadowOffset to behave as (you) intended on all versions of macOS, both before and after Catalina, the workaround is to check if (@available(macOS 10.15, *)) and switch the sign of your offset height accordingly.

Jonathan Deutsch:

Also your article surfaces for me that the number of mandatory @available checks to workaround bugs and API changes is really getting bad for my app.

@hypeapp runs on macOS 10.10+ and has 111 checks! These are for compatibility and not to make use of new OS features.

Also, some enum values have changed on Apple Silicon Macs:

The NSTextAlignment enumeration uses different numerical values for some constants on arm64 and x86_64 architectures. When referring to constants using numerical values, validate that you use the correct values on each architecture.

The NSImageResizingMode and UIImageResizingMode enumerations uses different numerical values for some constants on arm64 and x86_64 architectures. When referring to constants using numerical values, validate that you use the correct values on each architecture.

See TARGET_ABI_USES_IOS_VALUES in the headers. I’m not sure how this interacts with archiving.

7 Comments RSS · Twitter

Seems to be limited to NSAttributedString drawing, not NSShadow in general. My app versions compiled with Xcode 10 and 12 draw it as expected on macOS 10.14 and 10.15.

@Maxim Seems extra weird that it behaves differently depending on where the shadow is used.

NSTextAlignment's documentation says what the numeric values are. That's part of the public interface. Is Apple documentation saying that Apple documentation is wrong?

The change in NSTextAlignment bit my app since I had the values assigned as control/menu tags in certain XIB files. A generic action like -changeAlignment: would inspect the sender's tag to apply the desired alignment, which is now wrong on ARM.

I understand Apple's motivation here. Unifying these values to be the same on both platforms makes sense. I only wonder why UIKit didn't use the AppKit values from the very beginning?

I filed a documentation bug request with Apple about this discrepancy and recently got an answer:

Please note that we plan to update the header as the docs are correct.

Shadows when drawn through NSGraphicsContext and CGContext operate in their own coordinate systems. This allows them to project *down* and to the right, regardless of whether the coordinate system is flipped. Shadows are not always drawn this way, though. One common way that happens is by mapping an NSShadow to a CALayer’s shadow properties. CALayer and its shadow share a coordinate system, so the offset doesn’t always mean the same thing.

I see in Xcode 13.1 that the constants for NSTextAlignment have a Note saying that they have different values under certain conditions:

Declaration
NSTextAlignmentRight = 2
Discussion
Note
In Mac apps, including apps built with Mac Catalyst, the value of this enumeration case is 1.

The note seems incorrect, though: To me it suggests that the value is 1 in ALL Mac apps, but it's only the case for Intel arch, not for ARM arch.

Leave a Comment