原文链接:http://cocoadevcentral.com/articles/000088.php

官方文档:https://developer.apple.com/library/mac/documentation/UserExperience/Reference/ScreenSaver/Classes/ScreenSaverView_Class/index.html

written by Brian Christensen

Writing a screen saver module is surprisingly simple using Mac OS X's screen saver framework. The ScreenSaverView class provides us with an interface for animating the screen saver, drawing the preview view in the System Preferences pane, and displaying a configuration sheet.

I originally wrote this article in 2001 when it was still necessary to jump through a few configuration hoops just to create a new screen saver project. An entire section was dedicated to "Preparing the Project." Thankfully, these steps are no longer required. Ever since the advent of Xcode Apple has included a default screen saver project template to make our lives significantly easier.

Having said that, this is not just a mere recycling of the old article. There is a considerable amount of fresh material which I hope will improve upon the original article.

Part 1: basics on set up, simple bezier path drawing, and user preferences using a configure sheet
Part 2: advanced topics such as setting up and using OpenGL for drawing

Create a New Project
1 of 9

First, we'll need to create a new project. Launch Xcode and choose File > New Project. The window depicted below will appear:

From the list of choices you are presented with at this point select Screen Saver located under theStandard Apple Plug-ins category. Proceed to name the project MyScreenSaver (or any other name you prefer) and save it in your desired location. Once you have completed these steps, the project window will open.

Xcode automatically creates two files for us: MyScreenSaverView.h and MyScreenSaverView.m. Take a few minutes to poke around MyScreenSaverView.m and take note of the various methods that are provided for our use.

I have summarized some of these methods below:

ScreenSaverView: Key Methods
-initWithFrame:
 isPreview:
initializes the view with the given frame rect
-setAnimationTimeInterval: sets the animation rate in seconds
-startAnimation: invoked by the screen saver engine when animation should begin
-stopAnimation: invoked by the screen saver engine when animation should stop
-drawRect: draws the view
-animateOneFrame advances the animation by one frame at the rate set by the preceding method (this can be used for drawing instead of drawRect:)
-hasConfigureSheet returns true if the module has a configure sheet
-configureSheet returns the module's associated configure sheet

Implement the Animation Method
2 of 9

We want to draw a bunch of shapes of random type, size, color, and location. This drawing magic must occur either in the drawRect: or animateOneFrame method. Since animateOneFrame is slightly easier to use and better suits our purpose, this is the method we will be using.

Open the "MyScreenSaverView.m" file to edit it. Scroll to the animateOneFrame method and add the following code:

MyScreenSaverView.m
- (void) animateOneFrame{ NSBezierPath *path; NSRect rect; NSSize size; NSColor *color; float red, green, blue, alpha; int shapeType;

First of all, we want to obtain the screen's boundaries for later use. To do this, we use the boundsmethod. This returns an NSRect from which we will obtain the size property.

size = [self bounds].size;

Since we want our shapes to have random sizes, we can use the specially suppliedSSRandomFloatBetween() function for this. For those who are wondering, it is automatically seeded by the screen saver framework, so there is no need to manually deal with that.

// Calculate random width and height rect.size = NSMakeSize( SSRandomFloatBetween( size.width / 100.0, size.width / 10.0 ), SSRandomFloatBetween( size.height / 100.0, size.height / 10.0 ));

Now we want to calculate a random origin point for our shape. To do this, we use the handySSRandomPointForSizeWithinRect() function, which will do all the work for us.

// Calculate random origin point rect.origin = SSRandomPointForSizeWithinRect( rect.size, [self bounds] );
Create Rectangles and Ovals
3 of 9
Using NSBezierPath

In simple terms, an NSBezierPath object lets you draw paths consisting of straight and curved line segments. When put together these can form shapes such as rectangles, ovals, arcs, and glyphs. We will be using the first three in our code.

We want to randomly decide whether to create our NSBezierPath object as a rectangle, oval, or arc. In order to provide for greater flexibibility should you choose to invent additional shape types at a later time, we will utilize a switch statement.

// Decide what kind of shape to draw shapeType = SSRandomIntBetween( 0, 2 ); switch (shapeType) { case 0: // rect path = [NSBezierPath bezierPathWithRect:rect]; break; case 1: // oval path = [NSBezierPath bezierPathWithOvalInRect:rect]; break;
Create Arcs
4 of 9

The arc case is a bit more complicated, so we will go through it step-by-step. An arc consists of a center, a radius, a starting angle, an ending angle, and a direction. First, we will select random starting and ending angles between 0 and 360 degrees. (Naturally, as you can see in the code below, it wouldn't make sense for the ending angle to start before the starting angle. Hence, it will not necessarily fall between 0 and 360 degrees. It is actually chosen to be somewhere between the starting angle value and the starting angle value plus 360.)

case 2: // arc default: { float startAngle, endAngle, radius; NSPoint point; startAngle = SSRandomFloatBetween( 0.0, 360.0 ); endAngle = SSRandomFloatBetween( startAngle, 360.0 + startAngle );

Similarly, we now choose a random radius. Since we already did a calculation earlier to determine a random rectangle size, we will simply use either the width or the height of that size, whichever is the smallest. In addition, we also calculate a center point using previously generated origin and size values.

// Use the smallest value for the radius (either width or height) radius = rect.size.width <= rect.size.height ? rect.size.width / 2 : rect.size.height / 2; // Calculate our center point point = NSMakePoint( rect.origin.x + rect.size.width / 2, rect.origin.y + rect.size.height / 2 );

Finally, we are ready to construct our path. Unlike the above rectangle and oval cases, here we must first create an empty bezier path and then append the arc path to it.

// Construct the path path = [NSBezierPath bezierPath]; [path appendBezierPathWithArcWithCenter: point radius: radius startAngle: startAngle endAngle: endAngle clockwise: SSRandomIntBetween( 0, 1 )]; } break;}
Set a Color and Draw the Shape
5 of 9

Naturally, we want to use random colors for our shapes, too. Take special note of the alpha parameter, which will create a nice transparency effect.

// Calculate a random color red = SSRandomFloatBetween( 0.0, 255.0 ) / 255.0; green = SSRandomFloatBetween( 0.0, 255.0 ) / 255.0; blue = SSRandomFloatBetween( 0.0, 255.0 ) / 255.0; alpha = SSRandomFloatBetween( 0.0, 255.0 ) / 255.0; color = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha];

Last, but not least, use the set method to set the color used to draw our shape to the screen. In keeping with the tradition we have established so far, we will again randomly determine whether to draw a filled shape or an outlined shape (using either fill or stroke).

[color set]; // And finally draw it if (SSRandomIntBetween( 0, 1 ) == 0) [path fill]; else [path stroke];}
Try It Out
6 of 9

It's time to test the screen saver. Select Build > Build. When the process is finished, go to the Finder and open the build folder located inside of your MyScreenSaver project folder. You should see a MyScreeSaver.saver file in there.

To test your screen saver simply double-click the file. This will open System Preferences and present you with a dialog to allow you to automatically install the screen saver.

If you make changes in the code and rebuild your project later, the next time you double-click the file System Preferences will conveniently ask you if you want to replace the old version. (Please note that you may need to quit and relaunch System Preferences for it to reload your screen saver module.)

Set Up a Configure Sheet
7 of 9

Although we could actually stop here since we now have a fully functioning screen saver, we, of course, will not. We want to make our screen saver even better by providing a few customization options through the use of a so-called "configure sheet." To save a bit of work, I have prepared the NIB file.

A

 Download this file, and double-click it to decompress it.

B

Drag the ConfigSheet.nib file into the Xcode project'sResource group. Accept the default settings for adding the file to the project.

This NIB file has a bunch of outlets which we need to let our screen saver view know about. EditMyScreenSaverView.h and add the following code:

MyScreenSaverView.h
@interface MyScreenSaverView : ScreenSaverView { IBOutlet id configSheet; IBOutlet id drawFilledShapesOption; IBOutlet id drawOutlinedShapesOption; IBOutlet id drawBothOption;}

To let the "Desktop & Screen Saver" system preference pane know that it should enable the "Options" button for our screen saver, we need to change our hasConfigureSheet method to returnYES.

MyScreenSaverView.m
- (BOOL) hasConfigureSheet{ return YES;}

Furthermore, we need to load our NIB file and return a pointer to our sheet, which we do by implementing the following code in our configureSheet method.

- (NSWindow *)configureSheet{ if (!configSheet) { if (![NSBundle loadNibNamed:@"ConfigureSheet" owner:self]) { NSLog( @"Failed to load configure sheet." ); NSBeep(); } } return configSheet;}

Before we can do a quick sanity check test run, we need to add one more method.

- (IBAction)cancelClick:(id)sender{ [[NSApplication sharedApplication] endSheet:configSheet];}

To make sure that everything has gone smoothly so far, build the project and double-click the resulting MyScreenSaver.saver file to install it. Once System Preferences has opened, click the "Options" button for our screen saver and the configure sheet should appear.

Implement the User Defaults
8 of 9

Now that we have a working configure sheet, we'd like it to actually do something useful. Fortunately, implementing preferences in screen savers is not significantly different than in any other Cocoa application. We will use the ScreenSaverDefaults class, a subclass of NSUserDefaults. This is a convenient and simple way of dealing with user preferences.

The first step is to register our default values, the factory settings. First, add this constant somewhere towards the top of our ".m" file (directly under @implementation, for instance, would be fine):

MyScreenSaverView.m
static NSString * const MyModuleName = @"com.yournamehere.MyScreenSaver";

(N.B.: Ideally, the com.yournamehere.MyScreenSaver string should match the corresponding string you are using in your project settings.)

Now, add the missing lines to the initWithFrame:isPreview: method so that it looks like this:

- (id)initWithFrame:(NSRect)frameisPreview:(BOOL)isPreview{ self = [super initWithFrame:frame isPreview:isPreview]; if (self) { ScreenSaverDefaults *defaults; defaults = [ScreenSaverDefaults defaultsForModuleWithName:MyModuleName]; // Register our default values [defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: @"NO", @"DrawFilledShapes", @"NO", @"DrawOutlinedShapes", @"YES", @"DrawBoth", nil]]; [self setAnimationTimeInterval:1/30.0]; } return self;}

Trudging on, let's implement the okClick: method. This is the method that will be invoked when the user clicks "OK" in the configuration sheet, so what we need to do is simply update the defaults to reflect the settings selected in the sheet.

- (IBAction) okClick: (id)sender{ ScreenSaverDefaults *defaults; defaults = [ScreenSaverDefaults defaultsForModuleWithName:MyModuleName]; // Update our defaults [defaults setBool:[drawFilledShapesOption state] forKey:@"DrawFilledShapes"]; [defaults setBool:[drawOutlinedShapesOption state] forKey:@"DrawOutlinedShapes"]; [defaults setBool:[drawBothOption state] forKey:@"DrawBoth"]; // Save the settings to disk [defaults synchronize]; // Close the sheet [[NSApplication sharedApplication] endSheet:configSheet];}

Lastly, we want the sheet to reflect the values in the preferences file when it is displayed. Navigate to the configureSheet method and update it as necessary to contain the following code:

- (NSWindow *)configureSheet{ ScreenSaverDefaults *defaults; defaults = [ScreenSaverDefaults defaultsForModuleWithName:MyModuleName]; if (!configSheet) { if (![NSBundle loadNibNamed:@"ConfigureSheet" owner:self]) { NSLog( @"Failed to load configure sheet." ); NSBeep(); } } [drawFilledShapesOption setState:[defaults boolForKey:@"DrawFilledShapes"]]; [drawOutlinedShapesOption setState:[defaults boolForKey:@"DrawOutlinedShapes"]]; [drawBothOption setState:[defaults boolForKey:@"DrawBoth"]]; return configSheet;}
Use the Defaults In Our Animation
9 of 9

Now that we have the defaults set up and ready to go, we want to actually utilize them during our animation drawing. As you will see, doing so is quite simple. Scroll to the animateOneFramemethod and add the following line to the list of local method variables:

MyScreenSaverView.m
ScreenSaverDefaults *defaults;

Continue scrolling down to the last section of code in the same method, which should be this:

if ( SSRandomIntBetween( 0, 1 ) == 0 ) [path fill];else [path stroke];

Replace the above block with the following:

defaults = [ScreenSaverDefaults defaultsForModuleWithName:MyModuleName];if ([defaults boolForKey:@"DrawBoth"]){ if (SSRandomIntBetween( 0, 1 ) == 0) [path fill]; else [path stroke];}else if ([defaults boolForKey:@"DrawFilledShapes"]) [path fill];else [path stroke];

That's it. Hit build and take your new screen saver module for a test run.

Wrap Up
Finished MyScreenSaver project for Xcode

You've written your first screen saver for Mac OS X. Certainly you must be hungry for more, so take a look at Write a Screen Saver: Part II, which covers more advanced topics including the use of OpenGL to do the drawing. It also explains important issues regarding symbol clashes and what conventions to use to prevent such problems (of which you should be aware before you release a screen saver into the wild).

As always, let us know what you think about the tutorial.

安装步骤:

1、项目左侧---Products----show in Finder

2、安装,输入密码

3、预览


 Feedback

written by Brian Christensen

In part I of this article we learned how to create a screen saver module, how to do some simple animations with NSBezierPath, and how to set up a configure sheet. Although interesting effects can certainly be achieved using NSBezierPath, more complex animations are usually done through OpenGL. An introduction to OpenGL itself is beyond the scope of this article; however, there are a number of basic steps that have to be taken to set up a useable OpenGL environment, which are outlined in this tutorial.

Furthermore, even if you aren't interested in OpenGL, you should skip down to the section titledPrevent Symbol Clashes for some critical information on how to avoid a potential problem. Additionally, we briefly discuss how to make a screen saver module universal to get it ready for the coming onslaught of Intel-based Macs.

Part 1: basics on set up, simple bezier path drawing, and user preferences using a configure sheet
Part 2: advanced topics such as setting up and using OpenGL for drawing

Create a New Project
1 of 10

The first order of business is to create a new screen saver project. Name the project CoolScreenSaver (or any other name you like). Once the project window opens, selectLinked Frameworks under the Frameworks and Librariesgroup.

Proceed to choose Project > Add to Project... and navigate your way to /System/Library/Frameworks. Add theOpenGL.framework file.

Additionally, we need to import the appropriate OpenGL headers as well. OpenCoolScreenSaverView.h and add the following lines:

CoolScreenSaverView.h
#import <OpenGL/gl.h>#import <OpenGL/glu.h>

While we have the header file open, we might as well add the couple of code bits we're going to need. Add the appropriate statements so that your file looks like this:

@interface CoolScreenSaverView : ScreenSaverView { NSOpenGLView *glView; GLfloat rotation;}- (void)setUpOpenGL;@end
Prepare OpenGL
2 of 10

The way OpenGL is conventionally handled when used in screen saver modules is by adding an NSOpenGLView instance as a subview of the ScreenSaverView. We will do this inside ourinitWithFrame:isPreview: method. Let's begin by adding the first piece of code to the aforementioned method.

CoolScreenSaverView.m
- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview{ self = [super initWithFrame:frame isPreview:isPreview]; if (self) { NSOpenGLPixelFormatAttribute attributes[] = { NSOpenGLPFAAccelerated, NSOpenGLPFADepthSize, 16, NSOpenGLPFAMinimumPolicy, NSOpenGLPFAClosestPolicy, 0 }; NSOpenGLPixelFormat *format; format = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease];

At this point it is worth mentioning that one could actually pass nil as the pixel format for the NSOpenGLView. This would cause some form of default pixel format to be established for us. However, this behavior does not appear to be officially documented anywhere and, more importantly, if we want such basic features as depth testing we will need to specifiy our own format anyway.

We continue by instantiating our OpenGL view with a NSZeroRect frame size and the pixel format we just created. Then we add that view as a subview of our regular ScreenSaverView, invoke our set up method, and set the animation time interval.

glView = [[NSOpenGLView alloc] initWithFrame:NSZeroRect pixelFormat:format]; if (!glView) { NSLog( @"Couldn't initialize OpenGL view." ); [self autorelease]; return nil; } [self addSubview:glView]; [self setUpOpenGL]; [self setAnimationTimeInterval:1/30.0]; } return self;}

We might as well get the dealloc method out of the way:

CoolScreenSaverView.m
- (void)dealloc{ [glView removeFromSuperview]; [glView release]; [super dealloc];}
Initialize OpenGL
3 of 10

Recall that in our initWithFrame:isPreview: method we made an invocation to setUpOpenGL. This is where we will perform our initial OpenGL initialization.

The first thing we need to do is set the OpenGL context. You should invoke makeCurrentContextwhenever you make OpenGL calls to ensure that the proper context is ready to receive commands.

CoolScreenSaverView.m
- (void)setUpOpenGL{ [[glView openGLContext] makeCurrentContext];

Next, we do the usual OpenGL set up:

glShadeModel( GL_SMOOTH ); glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); glClearDepth( 1.0f ); glEnable( GL_DEPTH_TEST ); glDepthFunc( GL_LEQUAL ); glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

Finally, initialize our rotation variable. We will be using this for our animation.

rotation = 0.0f;}
Handle View Resizing
4 of 10

What would typically be in a reshape() function in regular OpenGL programs will go in oursetFrameSize: method. setFrameSize: is invoked at least once after the view is initialized, and may be invoked again at any time if the view resizes for some reason. This is perfect for our situation and is exactly when the reshape() function is normally called as well.

CoolScreenSaverView.m
- (void)setFrameSize:(NSSize)newSize{ [super setFrameSize:newSize]; [glView setFrameSize:newSize]; [[glView openGLContext] makeCurrentContext]; // Reshape glViewport( 0, 0, (GLsizei)newSize.width, (GLsizei)newSize.height ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 45.0f, (GLfloat)newSize.width / (GLfloat)newSize.height, 0.1f, 100.0f ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); [[glView openGLContext] update];}
Separate Drawing from State
5 of 10

It is generally a good idea to keep the actual drawing of our animation separate from our internal state. What this means is that drawRect: should be used to execute the necessary OpenGL commands to draw our animation to screen, and that animateOneFrame should update the state of our animation.

In the first article we didn't bother with this distinction since it was such a trivial example. However, animateOneFrame really has more to do with the screen saver engine's timer firing than an actual view update being necessary. If our drawing is kept in drawRect: then it is guaranteed that we are always updating our view at the right time. Similarly, since animateOneFrame is tied to the timer, we will always be updating our internal state at the appropriate time interval.

The animateOneFrame method being, in our case, the simpler of the two methods, we will take a crack at it first. The following code simply updates our rotation variable and then invokessetNeedsDisplay. Whenever drawing is done in drawRect: instead of in animateOneFrame, this invocation is required.

CoolScreenSaverView.m
- (void)animateOneFrame{ // Adjust our state rotation += 0.2f; // Redraw [self setNeedsDisplay:YES];}
Draw the Pyramid
6 of 10

Since interesting OpenGL animations tend to get quite complex and learning OpenGL is beyond the scope of this article, we will limit ourselves to drawing a trivial pyramid. (This was taken straight from one of the famed NeHe OpenGL Tutorials. If you want a more detailed explanation of what's going on in the code below, you are encouraged to check out that site.)

CoolScreenSaverView.m
- (void)drawRect:(NSRect)rect{ [super drawRect:rect]; [[glView openGLContext] makeCurrentContext]; glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glLoadIdentity(); glTranslatef( -1.5f, 0.0f, -6.0f ); glRotatef( rotation, 0.0f, 1.0f, 0.0f ); glBegin( GL_TRIANGLES ); { glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 0.0f, 1.0f, 0.0f ); glColor3f( 0.0f, 1.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, 1.0f ); glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( 1.0f, -1.0f, 1.0f ); glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 0.0f, 1.0f, 0.0f ); glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( 1.0f, -1.0f, 1.0f ); glColor3f( 0.0f, 1.0f, 0.0f ); glVertex3f( 1.0f, -1.0f, -1.0f ); glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 0.0f, 1.0f, 0.0f ); glColor3f( 0.0f, 1.0f, 0.0f ); glVertex3f( 1.0f, -1.0f, -1.0f ); glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, -1.0f ); glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 0.0f, 1.0f, 0.0f ); glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, -1.0f ); glColor3f( 0.0f, 1.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, 1.0f ); } glEnd(); glFlush(); }
Make It Non-Opaque
7 of 10

At this point you could build and try testing your screen saver. However, if you do, you will find with great disappointment that all you get is a blank screen. There is an interesting story going here with the way AppKit's view drawing is implemented. The drawRect: method is invoked up the view hierarchy until an opaque view is found. Given that our NSOpenGLView is a subview of the CoolScreenSaverView, and that NSOpenGLView is an opaque view (at least as of recent Mac OS X releases), the drawRect: invocation chain gets broken right there and never reaches the drawRect:we just implemented above. Hence the reason you were staring at a blank screen if you tried running your screen saver just now.

Fortunately, there is a pretty simple fix to this problem. Select File > New File... and create a newObjective-C class. Name it MyOpenGLView.m (and, of course, have it create the corresponding .h file along with it). Open MyOpenGLView.h and change it to be a subclass of NSOpenGLView.

MyOpenGLView.h
@interface MyOpenGLView : NSOpenGLView {}@end

Proceed to open the corresponding .m file and add the following method:

MyOpenGLView.m
- (BOOL)isOpaque{ return NO;}

Now go back to our CoolScreenSaverView.h file and add the following #import directive:

CoolScreenSaverView.h
#import "MyOpenGLView.h"

Furthermore, change the type of our glView instance variable from NSOpenGLView to MyOpenGLView:

MyOpenGLView *glView;

Correspondingly, CoolScreenSaverView.m's initWithFrame:isPreview: must be modified to reflect this change. Find the relevant line and change it the following code:

CoolScreenSaverView.m
glView = [[MyOpenGLView alloc] initWithFrame:NSZeroRect pixelFormat:format];

That's it. As you can see, we are merely using a subclass of NSOpenGLView that says it's non-opaque, therefore ensuring that our own drawRect: method gets a chance to perform its magic.

Prevent Symbol Clashes
8 of 10

Unfortunately, that's not all there is to it. Before you release a screen saver out into the wild, you need to take into account the issue of symbol clashes. Screen saver modules are nothing more than regular NSBundle plug-ins. When you open the Desktop & Screen Saver preference pane in the System Preferences application, it loads up all the individual .saver files. In short, this means that all the screen saver modules (meaning all the classes contained therein) end up sharing the same namespace. So, if two readers of this tutorial release screen savers but neglect to use unique class names (ie. they leave the names unchanged and use CoolScreenSaverView andMyOpenGLView), and a user downloads and installs both of those modules, at least one of the modules is not going to work properly. Both of them will probably end up displaying the same animation.

A good strategy for preventing this is to add a unique prefix (preferably a reverse domain name identifier) to every class in your project. So, for our example, we would call our classComCocoaDevCentral_CoolScreenSaverView. The custom NSOpenGLView subclass would be calledComCocoaDevCentral_CoolScreenSaver_MyOpenGLView. Let's modify our above example to reflect these changes. Open the relevant files and change the appropriate lines:

CoolScreenSaverView.h
@interface ComCocoaDevCentral_CoolScreenSaverView : ScreenSaverView
CoolScreenSaverView.m
@implementation ComCocoaDevCentral_CoolScreenSaverView

Note that for MyOpenGLView we use an additional project-specific prefix as well. Since you might use the same MyOpenGLView class in several projects, you need it to be unique in each case.

MyOpenGLView.h
@interface ComCocoaDevCentral_CoolScreenSaver_MyOpenGLView : NSOpenGLView
MyOpenGLView.m
@implementation ComCocoaDevCentral_CoolScreenSaver_MyOpenGLView

Granted, this does look quite ridiculous and it makes things a bit unwieldy. Luckily, there is a forgotten gem built-in to the Objective-C compiler that will help us out. It's called@compatibility_alias. Go back to MyOpenGLView.h and add the following line to the end of the file (after the @end).

MyOpenGLView.h
@compatibility_alias MyOpenGLView ComCocoaDevCentral_CoolScreenSaver_MyOpenGLView;

This tells the compiler to replace every instance of MyOpenGLView with our absurdly long actual class name. That way, instead of referencing our glView instance variable as:

ComCocoaDevCentral_CoolScreenSaver_MyOpenGLView *glView;

We can leave our code unchanged and keep using:

MyOpenGLView *glView;
More Symbol Clash Madness
9 of 10

Since we changed our principal class name, we need to make the project settings reflect that change as well. Open the Targets group in the left-hand pane of the project window and double-click the CoolScreenSaver target.

Click the Properties tab and change the Principal Class field to our new class name.

Furthermore, function symbols may also cause problems. The easy way to avoid clashes here is to either mark functions with the keywords static (if you define and use the function from within the same file) or __private_extern__ (if you call the function from multiple files).

You can always check what symbols your module is exporting by using the nm tool. For example, to check on CoolScreenSaver, cd into CoolScreenSaver.saver/Contents/MacOS and run nm -g CoolScreenSaver | grep -v " U ". This will give you the following output:

brian$ nm -g CoolScreenSaver | grep -v " U "00000000 A .objc_class_name_ComCocoaDevCentral_CoolScreenSaverView00000000 A .objc_class_name_ComCocoaDevCentral_CoolScreenSaver_MyOpenGLView

It is a good idea to run this check every time you are ready to publicly release a screen saver.

Make It Universal
10 of 10

As every Mac developer well knows by now, the Intel-powered Macs are just around the corner. Given that screen savers are plug-ins and as such will not be eligible for emulation by the Rosetta environment, it makes sense to start compiling screen savers as universal binaries as soon as possible.

The first step is to double-click the CoolScreenSaver group in the left-hand pane of the project window.

The second step is to click the Build tab and then select the Deployment configuration.

Now change the Architectures property to ppc i386.

Finally, click the General tab and set the Cross-Develop Using Target SDK option to Mac OS X 10.4 (Universal).

Close the window, set your active build configuration to Deployment, and build the project. To verify that your module is in fact a universal binary, you can cd into build/Deployment/CoolScreenSaver.saver/Contents/MacOS and run file CoolScreenSaver. You should see the following result:

brian$ file CoolScreenSaverCoolScreenSaver: Mach-O fat file with 2 architecturesCoolScreenSaver (for architecture ppc): Mach-O bundle ppcCoolScreenSaver (for architecture i386): Mach-O bundle i386
Wrap Up
Finished CoolScreenSaver project for Xcode

That's all there is to it. We now have an OpenGL screen saver free from any potential symbol clash problems, and it is ready to go on PowerPC and Intel Macs to boot.

As always, let us know what you think about the tutorial.

Acknowledgements

Special thanks to Mike Trent for sharing his insight on how to get the OpenGL drawing to work properly.

mac_ScreenSaver_第1个屏幕保护程序相关推荐

  1. html文件设置成mac屏保,Mac怎么设置屏幕保护?如何设置Mac屏幕保护程序?

    Mac电脑如何设置屏幕保护程序?屏保是我们显示个性的重要途径,也是保护我们的电脑安全的一个重要方法.MAC用户想要修改自己的屏保该怎么办呢?今天PC6小编就教大家如何在Mac系统下设置桌面背景以及屏幕 ...

  2. 询问HTG:白噪声屏幕保护程序,有效的文件命名以及从密码泄露中恢复

    Once a week we share three of the questions we've answered from the Ask HTG inbox with the greater r ...

  3. windows xp 屏幕保护程序_小礼物:3D Windows XP 屏幕保护程序

    航通社旗下公众号"56K小猫"(微信:modem56k),原创文章未经授权禁止转载.航通社微信:lifeissohappy 微博:@航通社 生 / 活 / 小 / 妙 / 招 从昨 ...

  4. 个性屏幕保护程序_Mac高清鸟瞰屏幕保护程序,酷毙了

    最新的Aerial for Mac配备了一个漂亮的屏幕保护程序,由在旧金山,纽约,伦敦,中国和许多其他地方录制的一系列航拍电影组成.如果您想在Mac上使用这些剪辑,现在有一种简单的方法可以实现. Ae ...

  5. html文件设置成mac屏保,如何将视频设置为Mac上的屏幕保护程序 | MOS86

    尽管您可能已经在享受Mac上预装的屏幕保护程序,但现在您可以添加一种新的屏幕保护程序到Mac.您现在可以将视频添加为屏幕保护程序. 输入SaveHollywood,一个Mac应用程序,允许您将视频设置 ...

  6. linux替换屏幕保护进程,有没有一个体面的方式来阻止linux中的屏幕保护程序?...

    我正在寻找一种体面的,不跛脚的方式来禁止xscreensaver,kscreensaver或gnome-screensaver,哪些可能正在运行,最好是以屏幕保护程序的方式,而且它绝对肯定必须执行得很 ...

  7. 液晶显示器不宜使用屏幕保护程序

        在使用台式电脑时,很多人都喜欢使用屏幕保护程序,当他们转为使用笔记本电脑时,这个好习惯也被保留了下来,但他们却不知屏幕保护程序对笔记本电脑非但没有任何好处,反而还会造成一些负面影响. 实际上屏 ...

  8. 计算机无法屏保,电脑屏幕保护程序为什么不能修改 原因及解决方法【详细介绍】...

    为何不能修改电脑屏幕保护程序? 虽然屏幕保护并不是专门为省电而设计的,但一般Windows下的屏幕保护程序都比较暗,大幅度降低屏幕亮度,有一定的省电作用. 现行显示器分为两种--CRT显示器和LCD显 ...

  9. 怎样保护计算机桌面不被更改,Win7屏幕保护程序不能修改怎么办 win7无法设置电脑屏幕保护程序如何解决...

    大家都知道当我们的电脑待机之后就会出现一个屏幕保护程序界面,可以更好的节省能耗,有些win7系统用户觉得默认的屏幕保护程序效果图标不好看,所以就想要将其修改,然而却发现屏幕保护不能修改,要怎么办呢,针 ...

  10. [bat] cmd命令进入用户登录界面和屏幕保护程序

    [bat] cmd命令进入用户登录界面和屏幕保护程序 cmd命令进入用户登录界面 rundll32.exe user32.dll,LockWorkStation cmd命令进入屏幕保护程序 C:\Wi ...

最新文章

  1. mysql parameters_MySqlCommand Command.Parameters.Add已过时
  2. 数学无用论??我们欠孩子真正的数学阅读
  3. java sessionid放入cookie_JAVA开发 SESSION和COOKIE的关系
  4. 2017.4.21 螺旋矩阵 思考记录
  5. c#值get、 set用法(包含自动转换的说明)
  6. Python 之父 Guido van Rossum 宣布
  7. 关于focus的概念
  8. 标准燃料计算机准,油表显示的油耗真的准确吗?教你最准确的油耗计算方法
  9. 微信公共号申请图文详解
  10. 新事业,新征程:换屏哥,您身边的手机维修专家
  11. IOl:从文件夹中找到后缀名为TXT的文件,然后复制到指定的文件夹
  12. oracle12C以逗号隔开怎么写,10g之后支持中文逗号作为查询分隔符
  13. STM32系统时钟详解
  14. JS 数组删除 splice和delete
  15. 艾恩技术access sql asp
  16. 科研成果 | 信道模型 | 原理及随机数仿真 | 均匀、正态、双高斯、瑞利、莱斯、对数正态、nakagami、Suzuki分布的随机数仿真(matlab)
  17. 根据value值动态的设置checkbox选中状态
  18. 高中信息技术python及答案_高中信息技术《Python语言》模块试卷.doc
  19. android编辑框禁用emoji表情和颜文字
  20. Ktor 2.0?半香不香的尴尬

热门文章

  1. Linux——clamAV查杀病毒与防护
  2. 二十一世纪计算|洪小文:帮助机器和人类共同学习
  3. httpclient-4.3.6工具类,方便直接使用
  4. PIE-engine APP 教程 ——太湖生态环境智能监测系统
  5. 非常精美的唐诗,无与伦比哦1
  6. Python网络爬虫与信息提取——网络爬虫Scrapy框架
  7. Mysql学习日记:L13-distinct关键字
  8. raid缓存策略设置
  9. PSINS源码阅读—STIM300/GNSS组合导航
  10. ERROR: child process failed, exited with error number 1