Press "Enter" to skip to content

Year: 2016

AppKit bug in 10.12.2: NSTextField with tabs in text displays incorrectly

UPDATE: The bug described below is fixed in macOS 10.12.4 and the workaround described is no longer necessary (except for backward compatibility).

In a recent macOS project I have an NSTextField that contains text with tab characters in order to display a data table. (The text is an NSAttributedString to which I apply an NSParagraphStyle with its tabStops property set to an array containing a number of right-aligned NSTextTabs.)

The text field was drawn as intended (with the text aligned to the tab stops) before upgrading to 10.12.2. After upgraded, the text was no longer aligned to the tab stops, and when using monospacedDigitSystemFont there were cosmetic glitches displayed as well:

Under time pressure for a release, I implemented the workaround of replacing the NSTextField with an NSTextView. (I did this dynamically since it is not obvious how to create “bare” NSTextViews in Xcode’s Interface Builder.) Using an NSTextView I was able to get the data table to display as I had intended.

When writing up this bug (#29758498; Radar, Open Radar) I tried to create a simple test case that would illustrate the issue, but on my first try to recreate the problem from scratch, the issue didn’t appear. Puzzled, I copied the code and .xib file from my application into the test project, and was able to reproduce the issue.

By examining the text of the .xib files, I discovered that my newly created .xib file has a content view with wantsLayer=”YES”, and in this case the NSTextField works as intended under 10.12.2; but the .xib file from my application (which was created before Xcode 8 was released) did not have a layer set on the content view, and this did not work under 10.12.2 (although it had worked fine on 10.12.1 and earlier). Setting a layer on the application’s window’s content view (in the View Effects tab of the Assistant Editor in Xcode) was enough to fix the issue and make the table display as intended:

(Yes, bridge players, those point counts are bogus; my test program is generating random values to insert into the table.)

My wider questions are:

  1. What happened? We know there were a lot of graphic updates in 10.12.2, but it’s odd that the problem only occurs with the system font. I haven’t investigated whether the font file itself has changed.
  2. I can’t believe I’m the only person that’s run into this (although my usual googling produced no other hits). Is this really an uncommon case? Or is this a(nother) gap in Apple’s test suite?
  3. If Apple is expecting that the default case for content views is to have wantsLayer set, then it should certainly make this more widely known. I couldn’t find any references to this issue in the AppKit release notes.
2 Comments

Reading text files with arbitrary line endings in Swift 3

It’s easy enough in Swift to get an array of strings representing the lines of a text file:

  let contents = try! String(contentsOf: textFileURL)
  let lines = contents.components(separatedBy: .newlines)

…but what if the input might be a DOS format text file? CharacterSet.newlines contains both "\r" and "\n", so the code above will divide the text at each of them, and the array of strings will contain an extra empty string between every line.

Turns Out™ that Foundation has a number of APIs that understand all the ways line endings are represented in text files. The easiest way to read a text file with arbitrary line endings is to use the enumerateSubstrings(in:options:body:) method, specifying EnumerationOptions.byLines. This uses getLineStart(_:end:contentsEnd:for:) to recognize the line endings (see the documentation thereof for details) and is powerful enough to handle files with mixed line endings.

(In Objective-C these are the -enumerateSubstringsInRange:options:usingBlock: and -getLineStart:end:contentsEnd:forRange: methods of NSString.)

It’s straightforward to wrap this into an extension on String:


extension String {
func separatedIntoLines() -> [String] {
var lines: [String] = []
let wholeString = self.startIndex..<self.endIndex
self.enumerateSubstrings(in: wholeString, options: .byLines) {
(substring, range, enclosingRange, stopPointer) in
if let line = substring {
lines.append(line)
}
}
return lines
}
}

The name separatedIntoLines() seems like the best choice to describe the work being done; the alternative of a computed property simply called lines seems a little too terse to me. UPDATE 2018-02-13: After reviewing the Swift 4 API, perhaps splitIntoLines() would be a better name.

Comments closed

WordPress, nginx, and category links

I’m dabbling in gaining experience with web services in my spare time. I decided to bring my blog up under nginx rather than Apache. Everything seemed fine except that the category links oddly stopped working and led to a 404 result from nginx. Googling was strangely unproductive at first until I realized that the common guidance for fixing permalinks (which I did not see a problem with) also applied to category links.

The solution is to modify the location / directive in the server block to include a last-change rewrite to feed the URI back to WordPress’s index.php, and more importantly, to remove the =404 that causes nginx to fail when it can’t match the permalink to a file on disk. Specifically, my location originally read:

location / {
    try_files $uri $uri/ =404;
}

and needed to be changed to:

location / {
    try_files $uri $uri/ /index.php?$args;
}

Now nginx will do the right thing. Yay.

Comments closed

VisualEffectPlayground in Swift

When searching for information on implementing vibrancy effects and NSVisualEffectView, developers often find references to the VisualEffectPlayground sample code presented by Apple at WWDC 2014. However, they soon quickly discover that Apple pulled the sample code from the Apple Developer site. Perhaps the application was thrown together for WWDC and didn’t meet the usual standard for Apple sample code; however, to date Apple has not seen fit to post any replacement. And so the search continues.

During my own search, I discovered that a Xamarin engineer named Oleg Demchenko had ported the sample code to Mono and posted it to the Xamarin samples site. In order to get a working sample application, I decided to port this code back to Swift and posted it to GitHub.

DISCLAIMER: I don’t know C# and I’m still learning Swift. Nevertheless, the resulting application appears to perform as expected (although I haven’t made an exhaustive test). I will certainly consider any pull requests.

1 Comment

Blocking autoplay videos on Macworld

I hate ’em, you hate ’em; even the writers hate ’em as well.  I finally got fed up enough to figure out how to completely block the autoplaying videos on the Macworld website.

Most instructions tell you to disable Flash, but I don’t even have Flash installed on my main computer (outside of the player embedded in Chrome, but I primarily use Safari).  It turns out that the Macworld website will then load a video player from Brightcove, so you have to block that too.

I’m using AdBlock, so I click its toolbar icon, choose Options, and go to “Manually Edit Your Filters”.  Click “Edit” and insert this text:

! Block autoplay videos on Macworld/PCWorld (need to block Brightcove as well)
www.macworld.com##DIV[class="video-wrapper small-player"]
www.pcworld.com##DIV[class="video-wrapper small-player"]
players.brightcove.net

That seems to be the lowest common division; higher level entities are named “how-to” or “security” or some such. Might as well get PCWorld while we’re in there. Don’t forget to click “Save”, and you’re done. (Also don’t forget to pay for AdBlock; I’m doing that right now…)

3 Comments