How to Make a Custom Screensaver for Mac OS X

Learn the basics of programming your own Mac OS X screensaver

Trevor Phillips
Jul 15, 2019 · 5 min read
Image for post
Image for post
The “Wirbel” Screensaver

Apple’s default screensavers for Mac OS X are nice, but they get boring after a while. Any true nerd would take matters into their own hands when that happens!

If you want to make your own custom screensaver for Mac OS X, this article is for you. We’ll walk through a tutorial for making a screensaver which emulates the game Pong. It’s going to be super simple — this is just to get you started with how to make a screensaver. By the end, it will look something like this:

Image for post
Image for post

If you want inspiration to make something else, more complex screensavers are available here.

Step 1: Project Setup

Screensavers for Mac OS X are developed with Xcode. The first thing you’ll need to do is create a new Xcode project with the category “Screen Saver.”

Image for post
Image for post

I named my project Pong, but you can name yours anything you’d like. As of Xcode 10.2.1, Xcode will generate some files: PongView.h, PongView.m, and Info.plist.

Since Objective-C isn’t the prettiest language and Swift is becoming more mainstream, we’re going to delete these auto-generated Objective-C files and create a new file, PongView.swift.

Image for post
Image for post

The structure of this Swift file is quite simple. You can copy-paste the template from below:

We will use the function draw() to render content for each frame of the screensaver animation.

We will use the function animateOneFrame() to update the “state” of the screensaver every time the animation timer fires (this timer is created and handled automatically by the OS). It’s important to call setNeedsDisplay(bounds) at the end of this function so that the OS knows to re-draw the screen.

Important: Since we got rid of the Objective-C stuff and are using Swift, the project’s Info.plist file needs to be updated. Prefix the value for the key “Principal class” with the name of the Xcode project. For example, my Xcode project is called Pong, so I changed the value from PongView to Pong.PongView.

Image for post
Image for post

Step 2: Implement Logic

The state of our pong screensaver is going to be tracked with just a few simple variables:

In the init() function, we’re going to configure the ball’s initial position and velocity. We set the initial position to the center of the screen and set the initial velocity to a random vector with magnitude 10.

Now we need some helper functions to determine when the ball makes contact with the edges of the screen and/or with the paddle. The following two functions will do the trick:

The function ballHitPaddle() is not 100% robust for verifying contact between the ball and paddle, I know. But for our purposes, it’s good enough.

Now, we’re going to use these helper functions to update the “state” of our screensaver in animateOneFrame().

The paddle is guaranteed to track the x-position of the ball. Therefore, it will always make contact with the ball before it bounces on the bottom of the screen (unless you set the ball’s velocity really high, in which case it could “jump through” the paddle).

Step 3: Draw Animations

The only thing left to do is to draw the ball and paddle in the draw() function. Note that we re-draw the background first before drawing anything else, otherwise the ball would leave a “trail” as it bounces around the screen.

We also define more helper functions to compartmentalize the drawing logic:

Step 4: Install and Debug

At this point, the screensaver is ready to go. You can find all of the code here.

To install the screensaver, press the “Run” button on Xcode, open the build product Pong.saver in Finder, and double-click it. The Settings application should open up, and you’ll be prompted to install the screensaver.

Image for post
Image for post

If you make changes to the screensaver and want to re-install it, it’s important to close the Settings application before trying to re-install your screensaver.

Additional Debugging

It’s not ideal to test your screensaver using the tedious process of opening your build product in Finder, closing the Settings application, re-installing the screensaver, and etc.

Moreover, this method does not allow you to set breakpoints, interact with lldb, or view console output. But there’s a way!

Your screensaver class inherits from ScreenSaverView, which itself inherits from NSView. This means that it can be added to a subview of a regular old Mac OS X application.

Image for post
Image for post

Just create a new Xcode project of type “Cocoa App.” Then, add your screensaver as a subview of the main NSViewController's view generated by Xcode. We need to artificially create a timer for the animation loop, but it’s a pretty simple fix.

When you run this Cocoa App from Xcode, all of the normal tools such as breakpoints and console logs will be available for your debugging pleasure, and developing your screensaver will be a lot easier!

Resources

This was just an introduction to developing a Mac OS X screensaver. Your possibilities are only constrained by Apple’s API, and there’s a whole lot more that can be done.

Image for post
Image for post

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store