Learning Cocoa With Objective-C, 2nd Edition PDF
Learning Cocoa With Objective-C, 2nd Edition PDF
info
Reviews
Examples
Reader Reviews
Errata
Copyright
Preface
Audience
About the Example Code
How This Book Is Organized
How to Use This Book
Conventions Used in This Book
How to Contact Us
Acknowledgments
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
Part V: Appendixes
Appendix A. Exercise Solutions
Section A.1. Chapter 2
Section A.2. Chapter 3
Section A.3. Chapter 4
Section A.4. Chapter 5
Section A.5. Chapter 6
Section A.6. Chapter 7
Section A.7. Chapter 8
www.it-ebooks.info
Colophon
Index
www.it-ebooks.info
Copyright 2002, 2001 O'Reilly & Associates, Inc. All rights reserved.
Printed in the United States of America.
Published by O'Reilly & Associates, Inc., 1005 Gravenstein Highway North, Sebastopol,
CA 95472.
O'Reilly & Associates books may be purchased for educational, business, or sales
promotional use. Online editions are also available for most titles (http://safari.oreilly.
com). For more information contact our corporate/institutional sales department: 800-9989938 or [email protected].
Nutshell Handbook, the Nutshell Handbook logo, and the O'Reilly logo are registered
trademarks of O'Reilly & Associates, Inc. Many of the designations used by manufacturers
and sellers to distinguish their products are claimed as trademarks. Where those
designations appear in this book, and O'Reilly & Associates, Inc. was aware of a trademark
claim, the designations have been printed in caps or initial caps. The association between
the image of an Irish setter and the topic of Cocoa is a trademark of O'Reilly & Associates,
Inc.
Apple Computer, Inc. boldly combined open source technologies with its own
programming efforts to create Mac OS X, one of the most versatile and stable operating
systems now available. In the same spirit, Apple has joined forces with O'Reilly &
Associates to bring you an indispensable collection of technical publications. The ADC
logo indicates that the book has been technically reviewed by Apple engineers and is
recommended by the Apple Developer Connection.
Apple, the Apple logo, AppleScript, AppleTalk, AppleWorks, Carbon, Cocoa, ColorSync,
Finder, FireWire, iBook, iMac, iPod, Mac, Mac logo, Macintosh, PowerBook, QuickTime,
QuickTime logo, Sherlock, and WebObjects are trademarks of Apple Computer, Inc.,
registered in the United States and other countries. The "keyboard" Apple logo ( ) is used
with permission of Apple Computer, Inc.
While every precaution has been taken in the preparation of this book, the publisher and
the author assume no responsibility for errors or omissions, or for damages resulting from
the use of the information contained herein.
www.it-ebooks.info
Preface
Like a finely tuned BMW, Mac OS X is the ultimate programming machine.
Under the hood lies a powerful Unix engine, named Darwin, developed via Apple's open
source initiative and based on FreeBSD 4.4 and the Mach 3.0 microkernel. On the outside
is a highly polished graphical user interface (GUI) whose usability can't be touched by any
desktop environment on the planet, including GNOME and KDE for Linux, as well as
Windows XP.
The newest cat on the block-Mac OS X 10.2 (code-named Jaguar)-takes desktop and
network computing to a new level. Jaguar, first introduced to developers as a pre-Alpha
release at Apple's Worldwide Developer Conference (WWDC) in May 2002 and later
released to the public on August 24, 2002, brings many changes and improvements to the
legacy set forth by the previous Mac OS X releases. These changes include several
additions to the Cocoa application programming interfaces (APIs), known as the Cocoa
frameworks, arguably the best GUI application development environment on the face of
the planet. An integrated set of libraries and runtime, Cocoa provides a rich infrastructure
on which to build great user applications.
When it comes to building Cocoa applications, developers can choose from three languages
to work with the Cocoa APIs: Objective-C, Java, and AppleScript. This new edition of
Learning Cocoa, retitled as Learning Cocoa with Objective-C and thoroughly revised and
updated for Jaguar, shows you how to get started with building Cocoa applications for Mac
OS X using the Objective-C binding to the Cocoa frameworks.
www.it-ebooks.info
The concepts learned in one chapter spill over to the next, and the sample programs you
build while reading along get more complex as you go deeper into the book. By the end of
the book, you will have learned enough about Cocoa and Objective-C to set you on your
way to higher learning, and for that, there are plenty of other books available:
While these books also deal with Cocoa programming with Objective-C, each book takes a
slightly different approach. Programming is a funny art, and sometimes it is invaluable to
see several approaches to the same subject matter. To be a true master of the craft, you'll
probably want to read each of these books and glean from each what you can.
[1]
In addition to this and the previously listed books, you also have a vast resource of
information at your fingertips in the form of Apple's own documentation. Installed on your
system along with the Developer Tools, Apple's docs can be found in /Developer /
Documentation in both PDF and HTML format. If you have a fast or constant link to the
Internet, you can save some space on your hard drive by dumping these docs in the Trash
and using the online documentation found at http://developer.apple.com.
When Apple updates their documentation, they often first post
the revisions online, so you might want to keep that URL handy.
Additionally, there are some online resources-mailing lists and web sites-that you should
subscribe to and read frequently. A listing of these resources can be found in Appendix B,
located at the back of this book.
[1]
Learn the ways of the Force, Luke-just stay away from the Dark Side.
www.it-ebooks.info
Audience
As the title implies, this is a "Learning" book-a book for newcomers to Cocoa and
Objective-C. This book assumes you have a basic knowledge of ANSI C and that you're
open to learning the concepts of object-oriented programming. If you're not familiar with C
and you haven't programmed with Java or some other compiled language, you might want
to hold off on reading this book just yet. Likewise, if you're already familiar with ObjectiveC or have programmed for NeXTSTEP, chances are this book will be too basic for your
liking. Not that you can't pick something up from reading it, but this book is better suited
for newcomers.
These books will introduce you to the concepts of programming with C, giving you
the foundation you need before reading this book.
Experienced NeXT developers
If you have worked with OpenStep or NeXTSTEP, you will probably find the
www.it-ebooks.info
material in this book too basic. You might use this book as a refresher to come up
to speed, but it probably won't be the Nirvana you're searching for.
Java developers
This book covers Cocoa using the Objective-C language. If you are a Java
developer and don't mind learning a new language (learning new languages is
always good for you!), then you will do fine with this book. However, if you want a
strict treatment of Cocoa with Java, this book is not for you.
www.it-ebooks.info
[2]
You
All of the examples have been tested using Mac OS X 10.2, Project Builder 2.0, and Interface Builder 2.1. If
you use this book with a later release of any of these products, the user interface and features may be different
from those shown in the book, but everything should work. However, because the examples utilize many
features first introduced with Jaguar, such as GCC 3
earlier release of Mac OS X with this book.
[3]
In some of the examples, we put a number (or letter, depending on the other elements on the page) on the right
side of any line of code that we explain in detail. Numbered explanations appear below a listing, as shown in
the following example:
int row = [itemList selectedRow];
NSString * newName = [[itemList selectedCell] stringValue];
// 1
// 2
1. The index of the row is obtained by passing the selectedRow message to the itemList object.
2. The newName string is obtained from the cell by using the stringValue message.
[2]
This book does not come with a CD-ROM. Bundling a CD would increase the cost of production and the cost to
you. It is our belief that anyone reading this book has access to an Internet connection and would rather save money
by simply downloading the example code off the Web.
[3]
GCC 3 introduces support for the C 99 standard, allowing us to make our example code more readable and easier
to understand.
www.it-ebooks.info
Part I
Cocoa Overview and Foundation introduces the Cocoa frameworks and describes the highlevel features they provide application programmers, as well as how they fit with other
Mac OS X frameworks. It also includes a brief introduction to object-oriented
programming, the Objective-C language, and Apple's development tools.
Chapter 1
Places Cocoa in the context of the Mac OS X programming environment and
introduces the frameworks and classes that make up the Cocoa API.
Chapter 2
Introduces Project Builder and Interface Builder, Apple's tools for Mac OS X
development. The chapter then goes on to describe the wide array of tools and
utilities available to assist in building, debugging, and performance-tuning
applications on Mac OS X.
Chapter 3
Explains the benefits of object-oriented programming practices (as compared to
procedural programming) and provides an introduction to the terminology and core
concepts needed to use the Cocoa frameworks effectively. It also includes a primer
on the Objective-C programming language.
Chapter 4
Provides a series of mini-tutorials to introduce the Cocoa Foundation, including
strings, arrays, collections, utility functions, and memory management.
www.it-ebooks.info
Part II
Single-Window Applications covers the basic building blocks of any Cocoa application that
displays a single GUI window to the user. This section uses a series of examples to
illustrate the concepts presented. The techniques and concepts you learn in each chapter
will lay the foundation for the next chapter.
Chapter 5
Introduces the Model-View-Controller (MVC) pattern and how Cocoa programs
are structured and developed. You will also learn about nib files and how to use
them in your applications.
Chapter 6
Goes into detail about how the windowing system works, as well as how to create
View and Controller objects to present a user interface.
Chapter 7
Cocoa's default set of controls covers most of the common UI needs that
applications have, but they can't cover everything. Your application may need to
present a specialized view onto a data source or simply draw arbitrary content to
the screen. This chapter shows how to create these custom views.
Chapter 8
Introduces the event loop and explains how events propagate along the responder
chain. It also covers how events are queued and dispatched, as well as how event
delegation works.
Chapter 9
Shows how to work with the data-bearing objects of an application. The chapter
also shows how this information can be utilized with the Controllers and Views of
an application and how it can be read from and written to storage.
Part III
Many applications today, such as word processors and web browsers, are built around the
concept of a document. Creating an application that can handle multiple documents is
www.it-ebooks.info
tedious in the best of times. Luckily, Cocoa provides the ability for an application to handle
multiple documents with ease. Document-Based Applications shows how to use Cocoa's
document architecture.
Chapter 10
Presents the basic concepts of the document-handling architecture and how
documents are managed. The chapter guides you through the process of creating an
application that takes advantage of the architecture.
Chapter 11
Shows advanced text-handling abilities of Cocoa, such as handling fonts, working
with layout managers, enabling rulers, and working with attachments.
Part IV
Miscellaneous Topics covers a variety of Mac OS X and Cocoa features that are important
to delivering finished applications and giving them their finishing touches. The chapters in
this part of the book cover diverse topics and can be read in any order.
Chapter 12
This chapter shows you how to add printing functionality to your application.
Chapter 13
Here we describe how bundles, application or otherwise, are structured, how icons
and document types are defined, and how application signatures work.
Chapter 14
Once you build an application, there are several ways to customize the interface to
accommodate users in different parts of the world.
Chapter 15
Mac OS X provides comprehensive management of user preferences. This chapter
explains how to work with this system to store information that can be used across
multiple invocations of your application.
Chapter 16
www.it-ebooks.info
Applications will often have more than just one interface component. Inspectors
and palettes abound in modern applications. This chapter shows in detail how to
store your user interface in multiple nib files to improve performance and ease
maintainability and localization.
Chapter 17
Once you build an application, there are several important things you should do to
make it ready for distribution. Cocoa provides default copyright strings and About
boxes that need to be edited, and you should probably create some sort of Help
documentation for the application. Finally, this chapter shows how to create an icon
for your application and add that to the application bundle as well.
Part V
The Appendixes include quick-reference material for learning more about Cocoa's
Objective-C classes and list resources that are beyond the scope of this book for expanding
your Cocoa development horizon.
Appendix A
Provides solutions to all of the exercises found at the end of each chapter.
Appendix B
Provides a valuable list of Cocoa-related resources and where to find them,
including Mac OS X's "built-in" developer documentation, books, mailing lists, and
web sites.
Appendix C
Provides a guide to the various API references available to you as a developer, as
well as some tools that will help you search and browse the available
documentation.
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
A carriage return ( ) at the end of a line of code is used to denote an unnatural line
break; that is, you should not enter these as two lines of code, but as one continuous
line. Multiple lines are used in these cases due to printing constraints.
%, #
The percent sign (%) is used in some examples to show the user prompt from the
tcsh shell; the hash mark (#) is the prompt for the root user.
Menu Symbols
When looking at the menus for any application, you will see some symbols
associated with keyboard shortcuts for a particular command. For example, to
create a new project in Project Builder, you would go to the File menu and select
New Project), or you could issue the keyboard shortcut,
New Project (File
Shift- -N.
You should pay special attention to notes set apart from the text with the following icons:
This is a tip, suggestion, or general note. It contains useful
supplementary information about the topic at hand.
www.it-ebooks.info
How to Contact Us
We have tested and verified the information in this book to the best of our ability, but you
may find that features have changed (or even that we have made mistakes!). As a
newcomer to Cocoa and a reader of this book, you can help us to improve future editions
by sending us your feedback. Please let us know about any errors, inaccuracies, bugs,
misleading or confusing statements, and typos that you find anywhere in this book.
Please also let us know what we can do to make this book more useful to you. We take
your comments seriously and will try to incorporate reasonable suggestions into future
editions. You can write to us at:
O'Reilly & Associates, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
(800) 998-9938 (in the U.S. or Canada)
(707) 829-0515 (international/local)
(707) 829-0104 (fax)
You can also send us messages electronically. To be put on the mailing list or to request a
catalog, send email to:
[email protected]
To ask technical questions or to comment on the book, send email to:
[email protected]
The web site for Learning Cocoa with Objective-C, Second Edition lists examples, errata,
and plans for future editions. You can find this page at:
http://www.oreilly.com/catalog/learncocoa2
For more information about this book and others, see the O'Reilly web site:
http://www.oreilly.com
www.it-ebooks.info
Acknowledgments
First and foremost, I'd like to thank my editor, Chuck Toporek, who talked me into writing
the new edition of this book (twice even) and alternately utilized the editor's whip and kind
words of encouragement to guide me toward its completion. Without him, his advice, and
his faith in me to get the job done, this book would not have happened. Also at O'Reilly, I'd
like to thank Jeff Holcomb, the copyeditor for this book; David Chu, who assisted Chuck in
pulling this book together for production; Brenda Miller, who produced the index; Derrick
Story, who encouraged my early efforts with Cocoa by letting me write for the O'Reilly
Network; and finally Tim O'Reilly, Michael Loukides, and Bob Eckstien, who always
knew that I would write a book for O'Reilly & Associates some day.
Thanks as well to all the people at Apple, especially to the original NeXT and Apple
documentation teams. For this new edition, we've changed the title, stripped the book down
to bare metal, and built it back up. Without the foundation provided by the original
documentation teams, the job would have been much harder. Also thanks to the many
Cocoa engineers at Apple for taking the time to hash over the outline for the revision, and
for reviewing drafts of the manuscript along the way. You guys know who you are.
Many thanks to the independent reviewers of this book, including Jo Davidson (who gave
up part of the Memorial Day weekend to help us meet our deadlines) and Mike Barron.
Special thanks to Jason Hunter, who gave me an author's insight into the writing process,
for helping me find the right metaphors in Chapter 3, and for always being there when
needed. In addition, many thanks to Wilfredo Snchez Vega, who got me hooked on Mac
OS X in the first place after my Windows laptop went through one of its periodic
meltdowns.
Music from many creative and talented people fueled the writing of this book. Among the
artists in heavy rotation in iTunes and on the iPod: Tori Amos, Bedrock, Blue Man Group,
BT, The Chemical Brothers, The Crystal Method, Darude, DJ Amber (from the San
Francisco Bay rave scene), DJ Dragn'fly (from the Sacramento rave scene), Brian Eno,
Fatboy Slim, The Future Sound of London, Juno Reactor, Moby, New Order, The Orb,
Orbital, Mario Piu, Prodigy, Rinocerose, Sasha, Squarepusher, Underworld, Paul van Dyk,
and many others.
And finally, thanks to all my family and friends who lent support to the book writing
process and who encouraged me to chase my dreams: Dad, who taught me everything I
needed to know after all; Mom, who brought me into the world; Mahaila, who probably
never expected that I-of all the people in the family-would write a book; my sisters Susan,
Illona, Joli, and Heather, as well as my friends Justyna Horwat and Jim Driscoll. Last, but
not least, I want to thank Eleo, who ended up thoroughly addicted to the wireless network I
www.it-ebooks.info
installed at her place so that I could work on her couch, tapping away on my Titanium
PowerBook until late in the night.
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
written for Mac OS 8 or Mac OS 9 that have not been updated to take full
advantage of Mac OS X. Classic is essentially a modified version of Mac OS 9
running inside a process that has special hooks into other parts of the operating
system. Over time, Classic is becoming less interesting as more applications are
ported to run natively in Mac OS X.
To some degree, all of these application environments rely on other parts of the system.
Figure 1-1 gives a layered, albeit simplified, illustration of Mac OS X's application
environments and their relationship to the other primary parts of the operating system.
Figure 1-1. Cocoa as part of Mac OS X's programming environment
As you can see from Figure 1-1, each of Mac OS X's application environments relies upon
functionality provided by deeper layers of the operating system. This functionality is
roughly broken into two major sections: Core Foundation, which provides a common set of
application and core services to the Cocoa, Carbon, and Java frameworks, and the kernel
environment, which is the underlying Unix-based core of the operating system.
[1]
Contrary to what you may have heard elsewhere, Carbon is not doomed to fade away over time.
This erroneous opinion seems to be caused by a misinterpretation of the word "transitional" to
mean that the API itself will be going away, rather than meaning it is the API to use to transition
older applications. Moving forward, it will remain one of the core development environments for
Mac OS X. In fact, Apple engineers are striving to enable better integration between Carbon and
Cocoa.
www.it-ebooks.info
www.it-ebooks.info
SPARC, Alpha, and PA-RISC architectures. Later, the frameworks and tools were revised
to run on other operating systems, such as Windows and Solaris. These revised frameworks
became known as OpenStep.
Fast forward to 1996. Apple had been working unsuccessfully on a next-generation
operating system, known as Copland, to replace the venerable Mac OS 7. Their efforts
were running amok and they decided to look outside for the foundation of the new OS. The
leading contender seemed to be BeOS, but in a surprise move, Apple acquired NeXT,
citing its strengths in development software and operating environments for both the
enterprise and Internet markets. As part of this merger, Apple embarked on the
development of Rhapsody, a development of the NeXTSTEP operating system fused with
the classic Mac OS. Over the next five years, Rhapsody evolved into what was released as
Mac OS X 10.0. As part of that evolution, OpenStep became Cocoa.
Mac OS X remains very much a Unix system; the Unix side of Mac OS X is just hidden
from users unless they really want to use it. Its full power, however, is available to you, the
programmer, to utilize. Not only can you take advantage of the power, you can actually
look under the hood and see how it all works. The source code to the underpinnings of Mac
OS X can be found as part of Apple's Darwin initiative (http://www.developer.apple.com/
darwin).
www.it-ebooks.info
device on which they are printed or displayed. Any time an image is displayed in a
Cocoa window or printed, its colors are automatically rendered correctly according
to any color profile embedding in the image along with profiles for the display or
printer.
Internationalization and localization
Cocoa's well-designed internationalization architecture allows applications to be
localized easily into multiple languages. Cocoa keeps the user-interface elements
separate from the executable, enabling multiple localizations to be bundled with an
application. The underlying technology is the same that is used by Mac OS X to
[3]
Because Cocoa uses Unicode as its native character set, applications can easily
handle all the world's living languages. The use of Unicode eliminates many
character-encoding hassles. To help you handle non-Unicode text, Cocoa provides
functionality to help you translate between Unicode and the other major character
sets in use today.
Text and fonts
Cocoa offers a powerful set of text services that can be readily adapted by textintensive applications. These services include kerning, ligatures, tab formatting,
and rulers, and they can support text buffers as large as the virtual memory space.
The text system also supports embedded graphics and other inline attachments.
You'll work this text system firsthand in Chapter 11.
Cocoa supports a variety of font formats, including the venerable Adobe PostScript
(including Types 1, 3, and 42), the TrueType format defined by Apple in the late
1980s and adopted by Microsoft in Windows 3.1, and the new OpenType format,
which merges the capabilities of both PostScript and TrueType.
Exported application services
Cocoa applications can make functionality available to other applications, as well
as to end users, through two mechanisms: scripting with AppleScript and via
Services.
AppleScript enables users to control applications directly on their system, including
the operating system itself. Scripts allow even relatively unskilled users to automate
common tasks and afford skilled scripters the ability to combine multiple
applications to perform more complex tasks. For example, a script that executes
when a user logs in could open the user's mail, look for a daily news summary
www.it-ebooks.info
message, and open the URLs from the summary in separate web-browser windows.
Scripts have access to the entire Mac OS X environment, as well as other
applications. For example, a script can launch the Terminal application, issue a
command to list the running processes, and use the output for some other purpose.
Services, available as a submenu item of the application menu, allow users to use
functionality of an application whenever they need to. For example, you can
highlight some text in an application and choose the "Make New Sticky Note"
service. This will launch the Stickies application (/Applications), create a new
Sticky, and put the text of your selection into it. This functionality is not limited to
text; it can work with any data type.
Component technologies
One of the key advantages of Cocoa as a development environment is its capability
to develop programs quickly and easily by assembling reusable components. With
the proper programming tools and a little work, you can build Cocoa components
that can be packaged and distributed for use by others. End-user applications are
the most familiar use of this component technology in action. Other examples
include the following:
Cocoa's component architecture allows you to create and distribute extensions and
plug-ins easily for applications. In addition, this component architecture enables
Distributed Objects, a distributed computing model that takes unique advantage of
Cocoa's abilities.
[2]
BSD stands for Berkeley Software Distribution. For more information about BSD and its
variants, see http://www.bsd.org/.
[3]
Mac OS X 10.2 ships with localizations in the following languages: English, German, French,
Dutch, Italian, Spanish, Japanese, Brazilian, Danish, Finnish, Korean, Norwegian, Swedish, and
both Simplified and Traditional Chinese. Apple might add to or modify this list at any time.
www.it-ebooks.info
The classes in Cocoa's Foundation framework provide objects and functionality that are the
basis, or "foundation," of Cocoa and that do not have an impact on the user interface. The
AppKit classes build on the Foundation classes and furnish the objects and behavior that
your users see in the user interface, such as windows and buttons; the classes also handle
things like mouse clicks and keystrokes. One way to think of the difference in the
frameworks is that Cocoa's Foundation classes provide functionality that operates under the
surface of the application, while the AppKit classes provide the functionality for the user
interface that the user sees.
www.it-ebooks.info
1.4 Languages
You can build Cocoa applications in three languages: Objective-C, Java, and AppleScript.
Objective-C was the original language in which NeXTSTEP was developed and is the
"native language" of Cocoa. It is the language that we will work with throughout this book.
During the early development of Mac OS X (when it was still known as Rhapsody), a layer
of functionality-known as the Java Bridge-was added to Cocoa, allowing the API to be
used with Java. Support has been recently added for AppleScript in the form of
AppleScript Studio, which allows AppleScripters to hook into the Cocoa frameworks to
provide a comprehensive Aqua-based GUI to their applications.
1.4.1 Objective-C
The brainchild of Brad Cox, Objective-C is a very simple language. It is a superset of
ANSI C with a few syntax and runtime extensions that make object-oriented programming
possible. It started out as just a C preprocessor and a library, but over time developed into a
complete runtime system, allowing a high degree of dynamism and yielding large benefits.
Objective-C's syntax is uncomplicated, adding only a small number of types, preprocessor
directives, and compiler directives to the C language, as well as defining a handful of
conventions used to interact with the runtime system effectively.
www.it-ebooks.info
Objective-C is a very dynamic language. The compiler throws very little information away,
which allows the runtime to use this information for dynamic binding and other uses. We'll
be covering the basics of Objective-C in Chapter 3. Also, there is a complete guide to
Objective-C, Inside Mac OS X: The Objective-C Language, included as part of the Mac OS
X Developer Tools installation. You can find this documentation in the /Developer/
Documentation/Cocoa/ObjectiveC folder.
1.4.2 Java
Java is a cross-platform, object-oriented, portable, multithreaded, dynamic, secure, and
thoroughly buzzword-compliant programming language developed by James Gosling and
his team at Sun Microsystems in the 1990s. Since its introduction to the public in 1995,
Java has gained a large following of programmers and has become a very important
language in enterprise computing.
Cocoa provides a set of language bindings that allow you to program Cocoa applications
using Java. Apple provides Java packages corresponding to the Foundation and
Application Kit frameworks. Within reason, you can mix the APIs from the core Java
packages (except for the Swing and AWT APIs) with Cocoa's packages.
1.4.3 AppleScript
For many years, AppleScript has provided an almost unmatched ability to control
applications and many parts of the core Mac OS. This allows scripters to set up workflow
solutions that combine the power of many applications. AppleScript combines an Englishlike language with many powerful language features, including list and record
manipulation. The introduction of AppleScript Studio in December 2001, as well as its
final release along with Mac OS X 10.2, allows scripters the ability to take their existing
knowledge of AppleScript and build Cocoa-based applications quickly using Project
Builder and Interface Builder.
Coverage of AppleScript Studio is beyond the scope of this book. To learn more about
AppleScript Studio, see Building Applications with AppleScript Studio located in /
Developer/Documentation/CoreTechnologies/AppleScriptStudio/
BuildApps_AppScrptStudio.
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
Threads
A thread is an executable unit that has its own execution stack and is capable of
independent input/output (I/O). All threads share the virtual-memory address space
and communication rights of their task. When a thread is started, it is detached from
its initiating thread and runs independently. Different threads within the same task
can run on different CPUs in systems with multiple processors.
Locks
A lock is used to coordinate the operation of multiple threads of execution within
the same application. A lock can be used to mediate access to an application's
global data or to protect a critical section of code, allowing it to run atomicallymeaning that, at any given time, only one of the threads can access the protected
resource.
Tasks
Using tasks, your program can run another program as a subprocess and monitor
that program's execution. A task creates a separate executable entity; it differs from
a thread in that it does not share memory space with the process that creates it.
Ports
A port represents a communication channel to or from another port that typically
resides in a different thread or task. These communication channels are not limited
to a single machine, but can be distributed over a networked environment.
Timers
Timers are used to send a message to an object at specific intervals. For example,
you could create a timer to tell a window to update itself after a certain time
interval. You can think of a timer as the software equivalent of an alarm clock.
www.it-ebooks.info
and deallocates them at the close of the current run loop. Understanding memory
management is important in creating successful Cocoa applications. We'll discuss
this critical topic in depth in Chapter 4.
Serialization and archiving
Serializers make it possible to represent the data that an object contains in an
architecture-independent format, allowing the sharing of data across applications. A
specialized serializer, known as a Coder, takes this process a step further by storing
class information along with the object. Archiving stores encoded objects and other
data in files, to be used in later runs of an application or for distribution. This topic
will also be covered in depth in Chapter 4.
Distributed objects
Cocoa provides a set of classes that build on top of ports and enable an interprocess
messaging solution. This mechanism enables an application to make one or more of
its objects available to other applications on the same machine or on a remote
machine. Distributed objects are an advanced topic and are not covered in this
book. For more information about distributed objects, see /Developer/
Documentation/Cocoa/TasksAndConcepts/ProgrammingTopics/DistrObjects/index.
html.
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
be monitored by a program.
Views
A view is an abstract representation for all objects displayed in a window. Views
provide the structure for drawing, printing, and handling events. Views are
arranged within a window in a nested hierarchy of subviews.
Panels
Panels are a type of window used to display transient, global, or important
information. For example, a panel should be used, rather than a window, to display
error messages or to query the user for a response to remarkable or unusual
circumstances.
The Application Kit implements some common panels for you, such as the Save,
Open, and Print panels. These common panels give the user a consistent look and
feel for performing common operations.
Controls and widgets
Cocoa provides a common set of user-interface objects such as buttons, sliders, and
browsers, which you can manipulate graphically to control some aspect of your
application. Just what a particular item does is up to you. Cocoa provides menus,
cursors, tables, buttons, sheets, sliders, drawers, and many other widgets.
As you'll find throughout this book, the Cocoa development tools provide quite a lot of
assistance in making your applications behave according to Apple's Human Interface
Guidelines. If you are interested in the details of these guidelines, read the book Inside Mac
OS X: Aqua Human Interface Guidelines, commonly known as the "HIG." You can find a
local copy of the HIG in /Developer/Documentation/Essentials/AquaHIGuidelines/
AquaHIGuidelines.pdf.
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
A project browser that manages all the resources of a project, allowing you to view, edit, and organize
your source files.
The ability to invoke the build system to build your projects and run the resulting program.
A graphical source-level debugger that allows you to walk through the code, set breakpoints, and examine
call stacks.
A code editor that supports language-aware keyword highlighting, delimiter checking, and automatic
indentation for many languages, including C, Objective-C, C++, Java, and AppleScript.
Project search capabilities that allow you to find strings anywhere in a project.
Source control management integration using the Concurrent Version System (CVS). CVS enables
development teams (local or distributed) to work together easily on the same source code base.
www.it-ebooks.info
The figures in this book use the Single Window environment, because this is the environment that we
www.it-ebooks.info
personally use. If you have used another IDE, such as CodeWarrior, that uses many windows and are
comfortable with that approach, you may want to select one of the other two options.
4. When you have finished the first-time configuration, Project Builder displays the Release Notes for the
particular version you are using. Important information often shows up in these release notes. After the
Show Release
first time you run Project Builder, you can access this information using the Help
Notes menu.
2.1.5.2 Creating a new project
New Project. Project Builder then displays the New Project
To create the "Hello, World" project, select File
Assistant, shown in Figure 2-5, which takes you through a few simple steps to create a new project.
Figure 2-5. Project Builder's New Project Assistant
The New Project Assistant lets you choose a project type. Based on the type of project you select, your Project
will be created with files that serve as a useful starting point. When you select a type of application here, Project
Builder creates it for you with a skeleton of the files that you will need for that particular application type. The
application types are as follows:
Application
Starting points for creating Cocoa applications (Objective-C- and Java-based), as well as Carbon- and
AppleScript-based applications
Bundle
Starting points for creating bundles that link against the Cocoa, Carbon, or Core Foundation frameworks
Framework
www.it-ebooks.info
Starting points for creating frameworks that link against either Cocoa or Carbon
Java
Starting points for developing Java applets or applications using either the AWT or Swing APIs
Kernel Extension
Starting points for developing both generic kernel extensions and IOKit drivers
Standard Apple Plug-ins
Starting points for developing palettes for Interface Builder, preference panes for the System Preferences
application, and screen savers
Tool
Starting points for creating command-line applications that link against the Core Foundation, Cocoa
Foundation, or Core Services frameworks
Throughout this book, we focus almost exclusively on two categories of applications: simple tools with no GUI
(called Foundation Tools) and applications with GUI windows. For this example, we will build a simple tool that
doesn't have a graphical interface. Proceed as follows:
1. Scroll down to the list of Tool choices, and select Foundation Tool from the list, as shown in Figure 2-5,
and click Next.
2. The Assistant gives you an opportunity to name your new project and choose a location in the filesystem
in which to save it. Type hello in the Project Name field, as shown in Figure 2-6.
3. If you Tab to the location field, you will see that Project Builder gives you the option of saving the project
in ~/hello. This will create a new directory in your home directory named "hello". However, for the
purpose of working through the examples in this book, we recommend that you change this to ~/
LearningCocoa /hello. That way, all of the projects you create with this book can be saved in the ~/
LearningCocoa directory.
4. Click Finish.
Figure 2-6. Naming a Project Builder project
www.it-ebooks.info
When you finish creating the project, the main project window opens, as shown in Figure 2-7.
Figure 2-7. Project Builder's main window
Notice that Project Builder uses hierarchical groups to organize the various parts of a project. In this project, these
groups are the following:
Source
This group contains main.m, the file that contains the main function that is the entry point for your
application.
Documentation
www.it-ebooks.info
[1]
This group contains references to the frameworks that the application imports to gain access to system
services.
Products
This group contains the results of project builds and is automatically populated with references to the
products created by each target in the project.
These groups are very flexible in that they do not necessarily reflect either the on-disk layout of the project or the
manner in which the build system handles the files. Their sole purpose is to help you organize the files in your
project. The default groups created for you by the templates can be used as they are or rearranged however you
like.
To see the source code for the application's entry point as shown in Figure 2-7:
1. In the Groups & Files list of Project Builder's main window, click the disclosure triangle to the left of the
Source group.
2. Click on the icon for the main.m file. You will see the contents of the file in the code editor.
The main.m file contains the entry point for the application. The Foundation Tool project template provides a
standard main function that prints "Hello, World!", so we don't even need to add any code.
import <Foundation/Foundation.h>
int main (int argc, const char * argv) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
NSLog(@"Hello, World!");
[pool release];
return 0;
// 1
// 2
// 3
// 4
// 5
// 6
}
Now let's walk through the code, line-by-line, so you can get a feeling for what's going on here:
1. Imports the Foundation framework. This directive is similar to #include, except that it won't include the
same file more than once.
2. Declares the standard C main function for a program. This function is where execution starts when the
program is started.
3. The NSAutoreleasePool is one of Cocoa's memory-management tools. We'll cover more about how
memory management works in Chapter 4.
4. The NSLog function works very much like printf in the C language. The difference is that NSLog
www.it-ebooks.info
takes an NSString object instead of a C string. The @" . . . " construct is a compiler directive that
creates an NSString object using the characters between the quotation marks.
5. This line contains another part of Cocoa's memory housekeeping that we will explain in depth later.
6. A return from the main function indicating a normal program exit.
Why does NSLog have the NS prefix? Simple: NS stands for NeXTSTEP. All of the
classes and functions in the Cocoa frameworks start with NS to help protect the
namespace in which all functions and classes exist from collisions. The continued use
of NS is a vestige that shows Cocoa's heritage.
www.it-ebooks.info
If you don't want Project Builder to question you to save files each time you try to
buid a project, you can change this option in Project Builder's Preferences. Select
Preferences
Building, and then select Always Save from
Project Builder
the pul-down menu next to Unsaved files.
Project Builder tells you that there was a syntax and parse error. To see where the error is, click on the syntax
error message, and Project Builder highlights the line about which the compiler is complaining. Unfortunately, the
compiler is not smart enough to figure out that we don't have a semicolon in the right place; it just notices that it
has run across some syntax that it did not expect. This is typical with syntax errors. If you don't see the problem at
first, look at the line of code above the reported line.
Add the missing semicolon back in after the NSLog function, and recompile by clicking the Build button to get a
working program again.
2.1.5.5 Running the application
Congratulations! You've just created your first Cocoa application and didn't even have to type in any code. All that
is left to do is click the Build and Run button, as shown in Figure 2-10. When the application launches, the Run
pane of Project Builder's main window will enlarge to display the output of the NSLog function.
www.it-ebooks.info
Figure 2-10. Project Builder's main window after running Hello World
In addition to the string, the NSLog function prints the current date and time, the program name, and the process
ID number (PID) of the program. Since this is a tool application with no GUI, you might want to see the behavior
of this program on the command line.
1. Open up a Terminal window, found in the /Applications/Utilities folder.
As with Project Builder, you may want to add the Terminal application to your
Dock for easy access, if you haven't already done so.
2. The hello executable is built into a subdirectory of your project. To run it, enter the following into the
Terminal window:
[localhost:~] duncan% LearningCocoa/hello/build/hello
When the program is run, you should see something similar to the following output:
2002-06-08 23:23:29.919 hello[490] Hello, World!
www.it-ebooks.info
The timestamp and process ID information come in handy when you are looking for the output from a program
that was launched from the Finder, but not from inside of Project Builder or from the command line. In those
cases, the output from NSLog will show up in the system's message log. You can easily view these messages
using the Console application, also found in the /Applications/Utilities folder.
If you have spaces in the folder in which you saved your program, the Terminal shell
will complain. The reason is that spaces are used to separate the arguments in a shell
command and must be escaped. If you saved your project into a ~/Learning Cocoa
directory (notice the space), your command would need to look like this:
[localhost:~] duncan% Learning\ Cocoa/hello/build/hello
The backslash in front of the space tells the shell that the space is part of the path of
the program.
www.it-ebooks.info
2. Now click the Build and Debug button. This will start the debugger and then load the hello program into
it. Execution will stop at the first statement after the breakpoint. In this case, it will stop at the first line of
our main method, as shown in Figure 2-12.
Figure 2-12. The debugger in action
Notice that Project Builder shows both a thread stack viewer and a variable viewer. The thread stack
www.it-ebooks.info
viewer shows execution stack. The main method is at the top of the stack, indicating that this is the method
within which execution is stopped. The variable viewer gives the values of all the arguments and variables
that are applicable in the function. Notice the value of the pool variable.
3. Click once on the Step over Function button (called out in Figure 2-12). Note that the current execution
highlighter moves to the next valid line of code. Also notice that the value of the pool variable is
highlighted in red. This means that the pool variable was just set. The value is actually a pointer to the
contents of the object in memory.
4. Click the Step over Function button once again. The NSLog function was called. To see the output, click
the Console tab above the variable viewer, as shown in Figure 2-13. Also notice tht the value of the pool
variable is no longer red. This highlighting lasts only one step after the contents of a variable change.
Figure 2-13. Console output in the debugger
5. Click the Continue execution button to let the program execute as normal. You can click the Restart button
(called out in Figure 2-12) to restart the program at the beginning of execution.
6. Click the Stop button in the toolbar to exit the debugger.
Now that we have explored how to say "Hello, World!" to the console, let's take a look at building a GUI
application that says hello in a much different way.
www.it-ebooks.info
[1]
Manpages are the standard form of Unix documentation for command line utilities and are written as plain text files
with nroff macros. See http://www.opensource.apple.com/projects/documentation/howto/html/
man_page_HOWTO.html for more information.
www.it-ebooks.info
Interface Builder generates nib files that are an archive of object instances and are
packaged up with your built application. Unlike the product of many user interface-building
systems, nib files are not generated code-they are true archived (also known as "freezedried") objects consisting of related user interface objects and supporting resources, along
with information about how the objects are related. The objects in the nib file are created
and manipulated using Interface Builder's graphical tools.
Interface Builder's standard palettes hold an assortment of AppKit components. Other
palettes can include Cocoa objects from other frameworks, third-party objects, and customcompiled objects.
www.it-ebooks.info
This will create a project that is set up as a simple Cocoa application. Go ahead and create
the project, giving it the name Hello World and saving into your ~/LearningCocoa folder.
When you have created the project, you will see a window similar to that shown in Figure 215.
Figure 2-15. Hello World application in Project Builder
www.it-ebooks.info
The Cocoa Application project type uses a different set of groups to organize projects than
those used in the Foundation Tool example. The groups in this project type are as follows:
Classes
This group is empty at first, but is used to hold the implementation (.m) and header (.
h) files for your project's classes.
Other Sources
This group contains main.m, the file containing the main function that loads the
initial set of resources and runs the application. In Cocoa applications with graphical
interfaces, you typically don't have to modify this file.
Resources
This group contains the nib files and other resources that specify the application's
GUI.
Frameworks
www.it-ebooks.info
This group contains references to the Frameworks (Foundation and AppKit) that the
application imports to gain access to system services.
Products
This group contains the results of project builds.
To see what Project Builder provides for you by default, go ahead and build and run the
project. A blank window should appear once Project Builder is done compiling everything.
Play with this window a little bit, and you'll notice that you can resize, minimize, and
maximize it.
Now, to finish our application, we should make it say "Hello, World" to us!
2.2.1.1 Open the main nib file
To begin constructing a user interface using Interface Builder, the first step is to open the
application's main nib file. Double-click MainMenu.nib in the Resources group of the
Groups & Files list of Project Builder's main window. This will launch Interface Builder (if
it is not already running) and open the nib file, as shown in Figure 2-16. A lot of windows
will appear. You might want to hide your other running applications so that you can
concentrate on just the windows that belong to Interface Builder. You can do this by using
Hide Others menu.
the Interface Builder
These are the various parts of Interface Builder (called out in Figure 2-16):
Figure 2-16. Interface Builder
www.it-ebooks.info
www.it-ebooks.info
application's nib files. For example, you can set both the size and initial location of an
application's main window by simply resizing and moving the window in Interface Builder.
1. Move the window near the upper-left corner of the screen by dragging the titlebar.
2. Make the window smaller by using the resize control at the bottom-right corner of
the window.
To create our application, we need to add a text label to the window.
1. Select the Cocoa-Views by clicking the second button from the left top of the Cocoa
objects palette window, as shown in Figure 2-17. If you don't see the Cocoa palette
window for some reason, select Palettes from the Tools menu to bring it forward.
Figure 2-17. The Cocoa-Views palette
2. Drag a System Font Text label from the palette onto the window.
3. Double-click on the new label and change the text to "Hello World".
4. Resize the interface window to a smaller size, and move the text label to the center
of the window. You should have something that looks similar to Figure 2-18.
Figure 2-18. Our finished Hello World interface
www.it-ebooks.info
Next, save your interface, as we are now done with Interface Builder. Some people refer to
saving the interface as "freeze-drying" it. All the various parts of the interface-definitions
about how they are related and connected-are saved in a form that can be quickly built up at
runtime. This process is also called " archiving" or "serialization."
To see the application in action:
1. Return to Project Builder.
2. Click the Build and Run button.
When the application runs, it opens up a window containing the Hello World text. Note that
you can resize the window (although the text doesn't stay centered, we'll learn how to do
that in later chapters), minimize it, maximize it, and close it. You can even quit the
application using the menu or the standard -Qkeyboard shortcut. All of this functionality
is simply "built-in" to Cocoa, allowing you to spend more time writing your applications
and less time taking care of details you shouldn't have to.
[2]
The name "nib" is an acronym for "NeXT Interface Builder," yet another vestige of Mac OS
X's heritage.
www.it-ebooks.info
Description
FileMerge
icns Browser
IconComposer
MallocDebug
ObjectAlloc
PackageMaker
www.it-ebooks.info
Pixie
Magnifies the screen area under the cursor, allowing you to see the
exact pixels comprising any onscreen object. Magnification is
adjustable from 1 to 12 times normal.
Quartz Debug
Sampler
Thread Viewer
Description
cc, gcc
gdb
www.it-ebooks.info
as
defaults
nibtool
libtool
otool
nm
pbxbuild
fixPrecomps
strip
cvs
sample
leaks
www.it-ebooks.info
Although the Mac OS X development environment contains many tools, the tutorials in
this book focus almost exclusively on the use of Project Builder and Interface Builder.
Some tools, such as the compiler, debugger, and linker, are usually invoked indirectly
through Project Builder when building a project. Others, such as ObjectAlloc, Quartz
Debug, and Sampler, are extremely useful to gain a deeper understanding of an
application's inner workings. Feel free to experiment with them at any point while working
through the tutorials in this book.
www.it-ebooks.info
2.4 Exercises
1. Locate the Project Builder and Interface Builder applications, and put them into the
Dock.
2. Locate the developer documentation, and place a shortcut to it in your Dock or in
your browser.
3. Watch the "Accessing API Documentation in Project Builder" movie at http://
developer.apple.com/techpubs/macosx/DeveloperTools/ProjectBuilderAccess/
index.html.
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
music than yours does. But just as all iPods have the same set of buttons - allowing the
same set of operations (play, stop, etc.) - all instances of a particular object expose the
same functionality to the outside world.
You specify an object by defining its class. Think of a class as a blueprint for making
object instances. It provides all the information needed to build new instances of an object.
Each class defines the internal variables that hold the data of an object instance and the
ways, or methods, by which that data can be manipulated. These methods define the
interface of the object. The interface is how other objects are allowed to use it.
On the back of every iPod is the phrase "Designed by Apple in Cupertino. Assembled in
Taiwan." This is a useful analogy for thinking about how classes and objects relate to each
other. In its corporate offices in California, Apple defined how an iPod operates and what
kinds of data it can store. Apple shipped those definitions to the factory in Taiwan that now
creates many unique instances of an iPod to ship to customers around the world. When you
create a class, you create a definition from which the runtime (the layer of software that
enables the object-oriented system to run) can create any number of objects (see Figure 32).
www.it-ebooks.info
In Objective-C, classes are more than just blueprints. They are actually first-class objects
themselves that can have methods associated with the class and not with its instances.
These are called class methods. Every object created has a reference to its own class. The
iPod analogy starts to get a bit stretched here, but imagine that each iPod had a reference to
the plans on which it was based and could consult them at any time. This is sort of what it
means for an object to look up its class object anytime it needs to do so.
3.1.2 Inheritance
We've defined a class to be a definition, or blueprint, from which object-oriented instances
are created. An iPod is an instance of the iPod class. But classes themselves can be defined
as specializations of other classes. For example, if you didn't know what an iPod was, you
would probably understand if I told you that it was a handheld MP3 player. In fact, all
handheld MP3 players share a certain number of characteristics. Like an iPod, a Rio can
hold and play MP3 files downloaded from a computer. It can't hold as many songs as the
iPod, but at least some of the functionality is the same.
The iPod is actually much more than a portable MP3 player. It's
also a bootable FireWire drive that can hold any kind of data that
you want it to hold. People are finding some pretty creative uses
for it beyond playing music. In Objective-C, objects that can
perform other functions can declare that they obey a particular
protocol, or way of behaving. We'll talk more about protocols
and how they can be used effectively in Chapter 9.
www.it-ebooks.info
Rio are both types of MP3 players. If we define a common MP3Player class, we can
gather certain aspects common to both devices into one class, as shown in Figure 3-3.
The iPod and Rio classes are both subclasses of the MP3Player class. Likewise, the
MP3Player class is the superclass of the iPod and Rio classes. Each subclass inherits
state (in the form of variable definitions) and functionality from the superclass. In this case,
both players inherit the same basic functions (play, stop, fast forward, etc.), but have very
different underlying implementations. The iPod uses a high-capacity hard drive while the
Rio uses flash memory.
Figure 3-3. Class hierarchy for the MP3Player class
Creating a new class is often a matter of specialization. Since the new class inherits all of
its superclass's behavior, you don't need to reimplement the things that work in the way that
you want. The subclass merely extends the inherited behavior by adding new methods and
any variables needed to support the additional methods. A subclass can alter superclass
behavior by overriding an inherited method, reimplementing the method to achieve a
behavior different from the superclass's implementation.
[3]
With Objective-C, a class can have any number of subclasses, but only one superclass.
This means that classes are arranged in a branching hierarchy with one class at the top-the
root class that has no superclass-as shown in Figure 3-4.
Figure 3-4. The root class in Objective-C
www.it-ebooks.info
NSObject is the root class of this hierarchy. From NSObject, other classes inherit the
basic functionality that lets them work in the system. The root class also creates a
framework for the creation, initialization, deallocation, introspection, and storage of
objects.
As noted earlier, you often create a subclass of another class because that superclass
provides most, but not all, of the behavior that you require. A subclass can have its own
unique purpose that does not build on the role of an existing class. To define a new class
that doesn't need to inherit any special behavior other than the default behavior of objects,
you make it a subclass of NSObject.
Inheritance is a powerful concept-one that many people new to
object-oriented programming tend to use too much. Used
inappropriately, it can lead to fragile software. In Cocoa, it's
often easier to use a new set of classes from a new class than to
use inheritance. This is called object composition. As you work
through this book, you'll see many examples of object
composition.
[1]
SIMULA I and SIMULA 67 were the first two object-oriented programming languages. They
were designed and built by Ole-Johan Dahl and Kristen Nygaard in Norway between 1962 and
1967.
[2]
[3]
There's even a decent chance that you might not like the music on my iPod, and vice versa.
www.it-ebooks.info
Tool
Foundation Tool)
2. Next, modify the main.m file, located in the "Source" group, so that it looks like Example 3-1. The Foundation
tool project template automatically generates some of this code. The lines that you need to add are shown in
boldface type.
Example 3-1. Creating objects
int main (int argc, const char * argv[]) {
NSAutoReleasepool *pool = [[NSAutoreleasepool alloc] init];
NSObject * object;
object = [NSObject alloc];
object = [object init];
NSLog(@"Created object: %@", object);
//
//
//
//
a
b
c
d
[pool release];
return 0;
}
Here's what the code that we added does:
a. Declares a variable named object of type NSObject. You should recognize this as a regular C
pointer.
b. Creates a new object of type NSObject and assigns it to the object variable. The alloc method
reserves (or allocates) memory space for the object and returns a pointer to that space. We'll explain
more about methods in just a bit.
c. Before an object is used in any way, it must be initialized. This init call initializes the object so it can
be used. The init method returns a fully initialized object ready for use. Since it is possible that the
init method will return a different object, we assign the return to the object variable again.
d. Prints a representation of the object to the console using a printf style format string with a %@ token,
indicating that the svalue of the object given after the format string should be printed.
There's actually a bit more going on in this code than what we've described. However, we'll fill in the missing
pieces as we go to avoid introducing too many concepts at once.
www.it-ebooks.info
3. Build and run the program. You should see something like this on the console:
2002-06-11 23:17:16.181 objects[477] Created object: <NSObject: 0x5ae90>
This tells us that we created an object of type NSObject that is located at the memory address 0x5ae90. This
isn't the most exciting information that could be printed, and it certainly won't win any user-interface awards,
but it shows us that objects are being created in the system by the runtime.
As a Cocoa programmer, you probably won't ever make direct use of the memory location
of the object instances you create. But under the hood, Cocoa uses this information to
locate and manipulate objects that you reference in code.
www.it-ebooks.info
Since objects should never be used without proper allocation and initialization, Objective-C programmers tend to
combine the methods into one line as shown in Example 3-2. Replace lines a, b, and c from Example 3-1 with the
single bolded line in Example 3-2.
Example 3-2. Combing object allocation and initialization
int main (int argc, const char * argv[]) {
NSAutoReleasepool *pool = [[NSAutoreleasepool alloc] init];
NSObject * object = [[NSObject alloc] init];
NSLog(@"Created object: %@", object);
[pool release];
return 0;
}
This shortens the allocation and initialization of an object to one line, ensuring that everything works properly, even in
the case where the init method of a class returns a different object than originally allocated. We will use this style of
object creation throughout the rest of the book.
www.it-ebooks.info
In this figure, the message is the expression enclosed in square brackets to the right of the
assignment operator (equals sign). The message consists of an object, known as a receiver,
and the name of a method to call on that object. In this case, the object is the NSObject
class, and the method to be called is the alloc method. In response to receiving this
message, the NSObject class returns a new instance of the class that will be assigned to
the variable anObject.
www.it-ebooks.info
In this figure, the message tells the runtime to call the setWidth: method and pass it the
argument width. Notice that a colon terminates method names that take an argument,
while method names that don't take an argument (like the alloc method in Figure 3-5)
don't have a colon.
Figure 3-7 shows a multiple-argument message. Here, the message and arguments are used
to set the width and height of the rectangle object to width and height, respectively.
This method is called the setWidth:height: method.
Figure 3-7. Objective-C message expression with multiple arguments
www.it-ebooks.info
The class object contains quite a bit of information about the internals of the class and how
it works. Part of this information is a method lookup table that maps selectors to methods,
as shown in Figure 3-10.
Figure 3-10. Method lookup table
www.it-ebooks.info
www.it-ebooks.info
Definition
id
Class
SEL
IMP
BOOL
nil
Nil
The id type can be used to type any kind of object, class, or instance. In addition, class
names can be used as type names to type instances of a class statically. A statically typed
instance is declared as a pointer to an instance of its class or to an instance of any class
from which it inherits.
www.it-ebooks.info
Tools
3. Name the file Song.m, as shown in Figure 3-12. Make sure that the Also create "Song.h" checkbox is clicked. This
creates the header file for the application's interface.
Figure 3-12. New Objective-C class Assistant
www.it-ebooks.info
Be careful not to confuse this use of the word interface with the term Graphical User
Interface. This use of the word refers to how components talk, or know, about each other and
doesn't refer to how users will interact with the program.
When you finish, Project Builder should look something like Figure 3-13. If Song.h and Song.m are not in the Source
category of files, you can simply drag them there. (Hint: Use the black insertion indicator that appears in the outline view
to guide you as you drag.) Where they appear doesn't matter to Project Builder, but keeping things neat and tidy will help
you, especially on larger projects.
Figure 3-13. Newly created class in Project Builder
www.it-ebooks.info
By creating the class header and implementation files, we have a start on a class that can be used in the rest of the program,
including our main function in the main.m file. Project Builder creates a basic Song.h header file for you. A new class is
declared in the header file with the @interface compiler directive. In this case, the directive is the following:
@interface Song : NSObject
This indicates that we are defining a class called Song that inherits from NSObject. The colon indicates the inheritance.
The rest of the file is left for us to complete. All the instance variables used by the class are declared between the brackets.
All the methods of the class are declared between the end bracket and the @end directive.
// a
// b
//
//
//
//
c
d
e
f
www.it-ebooks.info
// a
// b
- (void)setName:(NSString *)newName
{
[newName retain];
[name release];
name = newName;
}
// c
- (NSString *)artist
{
return artist;
}
// g
- (void)setArtist:(NSString *)newArtist
{
[newArtist retain];
[artist release];
artist = newArtist;
}
// i
@end
Here's what the additional code does:
1. Declares the name method that returns an NSString return value.
// d
// e
// f
// h
// j
// k
// l
www.it-ebooks.info
2. Returns the NSString object associated with the name instance variable.
3. Declares the setName: method that takes a single NSString argument.
4. Sends the retain message to the newName object. This tells the object that we intend to keep a reference to it.
This is part of Cocoa's memory management that will be described in depth in Chapter 4.
5. Sends the release message to the name object. If the name object is not pointing to an NSString object (if it
is pointing to nil), then this message will not do anything. However, if name had been set on this Song object
before, this message would tell the NSString object that we were not interested in it anymore.
6. Sets the name variable to point to the NSString object to which newName points.
7. Declares the artist method that returns an NSString return value.
8. Returns the NSString object associated with the artist instance variable.
9. Declares the setArtist: method that takes a single NSString argument.
10. Sends the retain message to the newArtist object, telling it that we are interested in keeping a reference to it.
11. Sends a release message to the existing object to which our artist variable points, if any.
12. Sets the artist variable to point to the NSString object to which newArtist points.
// a
// c
// d
[pool release];
return 0;
}
Here's what the additional code does:
www.it-ebooks.info
a. Imports the Song.h interface file, so we can use the Song class.
b. Allocates, initializes, and sets the name (setName) and artist (setArtist) of song1. The alloc and
init methods work just the same as they did with NSObject, since Song inherits them from
NSObject.
c. Allocates, initializes, and sets the name and artist of song2.
d. Prints the song1 and song2 objects, so we can see them.
2. Build and run the program. You should see something like this on the console:
2002-06-11 22:05:11.866 songs[7058] Song 1: <Song: 0x50f30>
2002-06-11 22:05:11.867 songs[7058] Song 2: <Song: 0x4f4b0>
You will recognize that this output is similar to the output that was printed from the NSObject object instances. This is
because the NSLog method actually calls the description method on an object, as defined by the NSObject class. To
change this to print something a bit more user-friendly, we somehow need to redefine what the description method
prints .
If you get a compiler error saying that the song2 variable is undeclared, chances are that you
are not using Mac OS X 10.2 (Jaguar). This book makes use of many of the new features of
Jaguar, including support for the C99 standard in GCC 3.1.
www.it-ebooks.info
// a
// b
@end
The code we added performs the following tasks:
a. Declares the description method that overrides the method by the same name in the NSObject class.
We don't need to declare this method in the Song.h interface file, as it is already part of the interface declared
by NSObject.
www.it-ebooks.info
b. Returns the name of the song as its description, using the special self variable that points to the object
under operation. We could have just returned the variable directly from this method, but using the [self
name] message means that if the internal implementation of the Song class changes, this method will work
correctly with no additional work.
2. Build and run the program. You should see the following output on the console:
2002-06-11 22:32:20.435 songs[7096] Song 1: We Have Explosive
2002-06-11 22:32:20.436 songs[7096] Song 2: Loops of Fury
Overriding the description method allows us to assign much more meaningful strings for output than NSObject's
default class name and memory address output.
www.it-ebooks.info
new object and return a substitute. Programs should therefore always use the object returned by init, and not necessarily
the one returned by alloc.
Subclass versions of init incorporate the initialization code for the classes from which they inherit through a message to
super. When working with classes that inherit from NSObject, a simple call to the superclass init method, as shown in
the following code block, is sufficient.
- init
{
[super init];
/* class-specific initialization goes here */
return self;
}
Note that the message to super precedes the initialization code added in the method. This ensures that initialization
proceeds in the order of inheritance.
However, since extending classes other than NSObject may return a different object than that on which the initializer was
called, you must be more careful in these cases and use the following code:
- init
{
if (self = [super init]) {
/* class specific initialization goes here */
}
return self;
}
Note that this code checks to see if super returned an object, or nil, before doing any initialization itself. This code will
work in any situation; however, none of the classes that we create in this book require these checks.
If you have been observant, you may have noticed that we have used two kinds of syntax to
denote comments. The first is the traditional /* . . . */ C-style comment. The second is
the newer // style comment that continues to the end of the line. You'll see both forms used
quite frequently in Objective-C code. There really aren't any guidelines as to which style should
be used where. You should simply use whichever works best, given the context of the comment.
www.it-ebooks.info
Typically, though not always, the designated initializer is the one with the most arguments. The
only way to determine the designated initializer of a class accurately is to read the
documentation for the class.
1. To work with designated initializers, edit Song.h and add the initializers, as shown in Example 3-8.
Example 3-8. Adding a designated initializer
#import <Foundation/Foundation.h>
@interface Song : NSObject {
NSString *name;
NSString *artist;
}
- (id)initWithName:(NSString *)newName artist:(NSString *)newArtist;
- (NSString *)name;
- (void)setName:(NSString *)newName;
- (NSString *)artist;
- (void)setArtist:(NSString *)newArtist;
@end
The code we added declares an initializer for our Song class that takes the name of the song as well as the artist.
2. Now add the initializer implementations to Song.m as shown in Example 3-9.
Example 3-9. Designated initializer implementation
#import "Song.h"
@implementation Song
- (id)init
// a
{
return [self initWithName:nil artist:nil];
}
- (id)initWithName:(NSString *)newName artist:(NSString *)newArtist
{
[super init];
[self setName:newName];
[self setArtist:newArtist];
return self;
}
- (NSString *)name
{
return name;
}
- (void)setName:(NSString *)newName
{
[newName retain];
[name release];
name = newName;
// b
//
//
//
//
c
d
e
f
www.it-ebooks.info
}
- (NSString *)artist
{
return artist;
}
- (void)setArtist:(NSString *)newArtist
{
[newArtist retain];
[artist release];
artist = newArtist;
}
- (NSString *)description
{
return [super description];
}
@end
The code we added in Example 3-9 performs the following tasks:
a. Overrides the init method provided by NSObject. This overridden method calls the new designated
initializer with nil string arguments for the name and artist arguments.
b. Declares our designated initializer with the same signature we used in Song.h.
c. Calls the init method of the NSObject superclass.
d. Sets the name of the new object.
e. Sets the artist of the new object.
f. Returns the freshly initialized object, ready for use.
3. Now, edit the main.m file to match Example 3-10.
Example 3-10. Using the designated initializer
#import <Foundation/Foundation.h>
#import "Song.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Song * song1 = [[Song alloc] initWithName:@"We Have Explosive"
artist:@"The Future Sound of London"];
Song * song2 = [[Song alloc] initWithName:@"Loops of Fury"
artist:@"The Chemical Brothers"];
NSLog(@"Song 1: %@", song1);
NSLog(@"Song 2: %@", song2);
[pool release];
return 0;
}
www.it-ebooks.info
In this code, we've simply replaced the longer three lines with our new initializer.
4. Build and run the program. You should see the following familiar output:
2002-06-11 23:08:07.783 Songs[7195] Song 1: We Have Explosive
2002-06-11 23:08:07.784 Songs[7195] Song 2: Loops of Fury
As you can see in Example 3-10, a line of code is often too long to fit on one line. Project
Builder has autoindentation functionality to make these constructs look good automatically, so
you don't have to type in a bunch of spaces manually. Simply go into Project Builder's
preferences, select the Indentation pane, and make sure that the "Syntax-aware indenting"
checkbox is checked.
// a
//
//
//
//
b
c
d
e
www.it-ebooks.info
{
return name;
}
- (void)setName:(NSString *)newName
{
[newName retain];
[name release];
name = newName;
}
- (NSString *)artist
{
return artist;
}
- (void)setArtist:(NSString *)newArtist
{
[newArtist retain];
[artist release];
artist = newArtist;
}
- (NSString *)description
{
return [self name];
}
@end
The code that we added in Example 3-11 performs the following tasks:
a. Declares the dealloc method. Note that since the dealloc method is defined by the NSObject class,
we don't have to declare it in the Song.h header file.
b. Prints out a message saying that the object is being deallocated.
c. Releases the name instance variable.
d. Releases the artist instance variable.
e. Calls dealloc on the superclass, allowing the deallocation functionality of the NSObject class to operate.
When you override the default dealloc functionality, you must always be sure to call dealloc in the
superclass.
2. Edit the main.m source file with the changes shown in Example 3-12.
Example 3-12. Releasing the song objects created
#import <Foundation/Foundation.h>
#import "Song.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Song * song1 = [[Song alloc] initWithName:@"We Have Explosive"
artist:@"The Future Sound of London"];
Song * song2 = [[Song alloc] initWithName:@"Loops of Fury"
www.it-ebooks.info
23:12:07.783
23:12:07.784
23:12:07.783
23:12:07.784
songs[7200]
songs[7200]
songs[7200]
songs[7200]
In Chapter 4, we present the finer details of memory management and explain why the act of releasing an object here calls
the dealloc method of our Song objects.
www.it-ebooks.info
www.it-ebooks.info
3.8 Exercises
1. Use the resources in Appendix C, and read the documentation for NSObject and
NSString.
2. Read the documentation for the NSLog function.
3. Investigate the isa and self variables by having the designated initializer of the
Song class print a description of the class.
www.it-ebooks.info
www.it-ebooks.info
4.1 Strings
So far, we have worked with strings using the @" . . . " construct in various method and function calls. This construct
is convenient when working with strings. When interpreted by the compiler, it is translated into an NSString object that
is based on the 7-bit ASCII-encoded string (also known as a "C string") between the quotes. For example, the statement:
NSString * lush = @"Lush";
is functionally equivalent to:
NSString * lush = [[NSString alloc] initWithCString:"Lush"];
NSString objects are not limited to the ASCII character set; they can handle any character contained in the Unicode
character set, allowing most of the world's living languages to be represented. Unicode is a 16-bit-wide character set, but
can be represented in 8-bits using the UTF-8 encoding.
- (const char *)
UTF8String
Returns a representation of the string as a UTF-8 representation. UTF-8 allows the transmission of Unicode
characters over channels that support 8-bit encodings. All of the lower levels of Mac OS X - including the HFS+
and UFS filesystems, as well as the BSD system routines - can handle char * arguments in the UTF-8 encoding.
- (NSString *)
stringByAppendingString:
www.it-ebooks.info
(NSString *)aString
Returns a new string object by appending the given string to the string upon which the method is called.
To explore these methods, we'll create a simple program using the following steps:
1. In Project Builder, create a new Foundation Tool (File
"strings", and save it in your ~/LearningCocoa folder.
New Project
Tool
You may have noticed that sometimes our project names start with a lowercase letter
and sometimes with an uppercase letter. The common practice in naming applications is
that command-line applications should be lowercase and GUI applications should be
initial capitalized. We'll use this practice through this book.
2. Open the main.m file, located in the "Source" group, and modify it to match the code shown in Example 4-1.
Example 4-1. Working with strings
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * artist = @"Underworld";
// a
// b
[pool release];
return 0;
}
The code we added in Example 4-1 performs the following tasks:
a. Declares an object of type NSString, named artist, and sets it to the value "Underworld"
b. Obtains the length of the string and prints it using the NSLog function
3. Build and run ( -R) the application. You will be prompted to save the main.m file, and then Project Builder will
compile and the run the code. You should see the following output in Project Builder's console:
2002-06-17 23:29:32.344 strings[1147] Underworld has length: 10
As we have seen before, the NSLog function prints the current date and time, the program name, and the process ID
(PID) of the program, as well as the output that we told it to print. In the output, we see that the artist object was
substituted for the %@ token and that the return value from the length method was substituted for the %d token.
Remember, you can use any of the standard printf substitution tokens in the format string, in addition to the %@
token.
4.1.1.1 Setting breakpoints and debugging
Instead of adding code to the strings tool, we will use the debugger to explore the UTF8String and
stringByAppendingString methods. This will give you some practice using the debugger, while you learn about
these methods.
www.it-ebooks.info
1. Set a breakpoint between the NSLog function and the [pool release] line of code in main.m. Remember to
set a breakpoint, click in the column on the left side of the code editor. If you want to move the breakpoint, click
and drag the breakpoint to its new location. In our code, this breakpoint is at line 8. An example of the breakpoint
set is shown in Figure 4-1.
Figure 4-1. Setting a breakpoint in the main.m file
www.it-ebooks.info
The debugger console behaves similarly to working with the Terminal application. You
enter a command, hit Return, and the result of the command is shown on the next line.
Just like the default shell in the Terminal, the debugger maintains a history of
commands that you can access by hitting the up and down arrows on your keyboard.
4. Type in print-object artist at the (gdb) prompt in the debugger console. You may have to click in the
debugger console to give it focus, so that you can enter commands.
(gdb) print-object artist
When you enter this command, the debugger outputs the following:
Underworld
In addition to simply printing objects, we can print the result of any message that we can send to an object. This
functionality is incredibly useful when trying to find the various states of an object while using the debugger.
5. Enter in the following into the debugger console:
(gdb) print-object [artist description]
The following result, matching what we just saw in Step 4, will be printed:
Underworld
6. Let's see the stringByAppendingString method in action. Enter the following into the debugger console:
(gdb) print-object [artist stringByAppendingString:@": Pearl's Girl"]
The debugger outputs the following result of the method call:
Underworld: Pearl's Girl
www.it-ebooks.info
7. You can also send messages to NSString objects created using the @"..." construct. Enter the following into
the debugger console:
(gdb) print-object [@"The artist is: " stringByAppendingString:artist]
The debugger outputs:
The artist is: Underworld
The next debugger command we will learn is the print command. This command prints out C types instead of objects.
We will use the print command to evaluate the return values of the length and UTF8String methods.
8. Enter the following into the debugger console:
(gdb) print (int) [artist length]
The debugger outputs:
$1 = 10
The $1 symbol is a temporary variable that holds the results of the message, and the 10 denotes the number of
characters (or length) in the artist object. Note that we needed to cast the return type from the length
message so that the print command could operate. Try this again without the (int) cast.
9. To see the UTF8String method in action, enter the following:
(gdb) print (char *) [artist UTF8String]
The debugger outputs something similar to the following:
$2 = 0x9f738 "Underworld\000"...
This is the null-terminated char * string representation, in UTF-8 encoding, of our artist string.
To quit the debugger, you can either click the stop button or enter quit at the (gdb) prompt.
www.it-ebooks.info
www.it-ebooks.info
New Project
Tool
2. Open the main.m file, located in the "Source" group, and modify it to match the code shown in Example 4-2.
Example 4-2. Working with substrings
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * song = @"Let Forever Be,The Chemical Brothers";
NSRange range = [song rangeOfString:@ ","];
printf("comma location: %i\n", range.location);
// a
// b
// c
// d
// f
// g
// e
[pool release];
return 0;
}
The code we added in Example 4-2 performs the following tasks:
a. Declares a string object named song and sets it.
b. Obtains the range of the comma in the song string.
c. Prints the location of the comma. Notice that we are using the standard C printf function here. We will
use printf instead of NSLog in many of the upcoming exercises, so the output from our programs won't
be cluttered with timestamps and PIDs. Note that, unlike the NSLog function, we have to be sure to include
the \n character to print out the new line.
d. Declares a string named title and sets it to the substring, from the start of the song string to the location
of the comma.
e. Declares a string named artist and sets it to the substring, from the comma to the end of the song string. We
use the range.location + range.length construction so that we find the index just after the comma
value. If we just used the location of the comma, it would show up in our substring.
f. Prints the title to the console, using the UTF-8 representation of the string. Notice that we are using the
printf %s token.
g. Prints the artist to the console, using the UTF-8 representation of the string.
3. Build and run ( -R) the application. You will be prompted to save your files, and then Project Builder will
compile and run the code. You should see the following output in the console:
comma location: 14
title: Let Forever Be
artist: The Chemical Brothers
www.it-ebooks.info
Some of the methods that you frequently will use with mutable strings are the following:
- (void)
appendString:
(NSString *)aString
Adds the characters of the given string to those already in the mutable string object upon which the method is
called.
- (void)
deleteCharactersInRange:
(NSRange)range
Deletes the characters in a given range.
- (void)
insertString:
(NSString *)aString
atIndex:
(unsigned index)
Inserts the characters of the given string into the mutable string at the location specified by the index. All of the
characters from the insertion point to the end of the mutable string are shifted to accommodate the new characters.
To explore these methods, we'll create yet another simple program, using the following steps:
1. In Project Builder, create a new Foundation Tool (File
New Project
"mutablestrings", and save it in your ~/LearningCocoa folder.
Tool
2. Open the main.m file, located in the "Source" group, and modify it to match the following code:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableString * song = [[NSMutableString alloc] init];
// a
www.it-ebooks.info
// b
// c
// d
// e
// f
// g
// h
[song release];
// i
[pool release];
return 0;
}
The code we added performs the following tasks:
a. Creates a new empty mutable string named song.
b. Appends the contents of the "Deaf Leppard" string to the song mutable string.
c. Prints the song mutable string to the console.
d. Gets the range of the "Deaf" substring.
e. Replaces the "Deaf" substring with "Def" to correct the misspelling.
f. Prints the song mutable string to the console.
g. Inserts the string "Animal by" at the beginning the mutable string.
h. Once again prints the song mutable string.
i. Releases the song object. Because we created the Song object using the alloc method, we are
responsible forr releasing it. We'll explain more about how this works later in this chapter.
3. Build and run (
-R) the application. You should see the following output in the console:
Deaf Leppard
Def Leppard
Animal by Def Leppard
www.it-ebooks.info
www.it-ebooks.info
New Project
Tool
2. Open the main.m file, located in the "Source" group, and modify it to match the code shown in Example 4-3.
Example 4-3. Reading files into strings
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// a
// b
// c
// d
// e
[pool release];
return 0;
}
The code we added in Example 4-3 performs the following tasks:
a. Creates a string object, named filename, that contains the path to the main.m source file of this project.
Note that you must save your project in your ~/LearningCocoa folder for this example to work. If you are
saving your projects to some other location, you will need to edit the path appropriately.
b. Sets the filename variable to a standardized path. This will resolve the ~/ characters to your home
directory.
c. Prints the resolved filename variable.
d. Creates a new string, named source, with the contents of the main.m source file.
e. Prints the source string to the console.
3. Build and run (
-R) the application. You should see output similar to the following appear in the console:
/Users/duncan/LearningCocoa/filestrings/main.m
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * filename = @"~/LearningCocoa/filestrings/main.m";
filename = [filename stringByStandardizingPath];
printf("%s\n", [filename UTF8String]);
NSString * source = [NSString stringWithContentsOfFile:filename];
www.it-ebooks.info
6. Click on the Console tab above the variable viewer to open up the debugger console.
7. Type in the following at the (gdb) prompt:
(gdb) print-object [filename lastPathComponent]
When you enter this command, the debugger should output the following:
main.m
8. Type in the following at the (gdb) prompt:
(gdb) print-object [filename pathExtension]
When you enter this command, you should see the following:
m
9. Quit the debugger; use the Stop button, or type in quit at the (gdb) prompt and hit return.
Now that we've covered quite a few things that you can do with strings, it's time to look at Cocoa's collection classes.
www.it-ebooks.info
4.2 Collections
Cocoa provides several classes in the Foundation Kit whose purpose is to hold and organize instances of other classes. These are
called the collection classes. There are three primary flavors of collections in Cocoa: arrays, sets, and dictionaries. These classes,
shown in Figure 4-3, are extremely useful in Cocoa application development, and their influence can be found throughout the Cocoa
class libraries.
Figure 4-3. Cocoa collection classes
Collection classes, like strings, come in two forms: mutable and immutable. Immutable classes allow you to add items when the
collection is created, but no further changes are allowed. On the other hand, mutable classes allow you to add and remove objects
programmatically after the collection is created.
Much of the power of collection classes comes from their ability to manipulate the objects they contain. Not every collection object
can perform every function, but in general, collection objects can do the following:
Derive their initial contents from files and URLs, as well as other collections of objects
Add, remove, locate, and sort contents
Compare their contents with other collection objects
Enumerate over their contents
Send a message to the objects that they contain
[1]
4.2.1 Arrays
Arrays-instances of the NSArray class-are ordered collections of objects indexed by integers. Like C-based arrays, the first object
in an array is located at index 0. Unlike C- and Java-based arrays whose size is set when they are created, Cocoa mutable array
objects can grow as needed to accommodate inserted objects.
The NSArray class provides the following methods to work with the contents of an array:
- (unsigned)
count
Returns the number of objects currently in the array.
- (id)
objectAtIndex:
(unsigned)index
www.it-ebooks.info
Returns the object located in the array at the index given. Like C- and Java-based arrays, Cocoa array indexes start at 0.
- (BOOL)
containsObject:
(id)anObject
Indicates whether a given object is present in the array.
To practice working with arrays do as follows:
1. In Project Builder, create a new Foundation Tool (File
and save it in your ~/LearningCocoa folder.
New Project
Tool
2. Open the main.m file, located in the "Source" group, and modify it to match the following code:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * string = @"one two buckle my shoe";
NSArray * array = [string componentsSeparatedByString:@" "];
int count = [array count];
int i;
for ( i = 0; i < count; i++ ) {
printf("%i: %s\n", i, [[array objectAtIndex:i] UTF8String]);
}
// a
// b
// c
// d
[pool release];
return 0;
}
The code we added performs the following tasks:
a. Declares a new string.
b. Creates an array of string objects using the componentsSeparatedByString: method of the NSString
class. Note that in the first example of this chapter, where we looked for the range of the comma to split the spring,
we could have used this method to get the two strings.
c. Obtains the count of the array to use in the for loop.
d. Prints each item of the array to the console.
3. Build and run (
0:
1:
2:
3:
4:
-R) the application. You should see output similar to the following appear in the console:
one
two
buckle
my
shoe
www.it-ebooks.info
2. Build and debug (
-Y) the application. Execution will start and then pause at the breakpoint we set.
www.it-ebooks.info
fill the gap.
- (void)
removeObject:
(id)anObject
Removes all occurrences of an object in the receiving array. The gaps left by the objects are removed by shifting the
remaining objects.
The following steps will explore these methods:
1. In Project Builder, create a new Foundation Tool (File
New Project
"mutablearrays", and save it in your ~/LearningCocoa folder.
Tool
2. Open the main.m file, located in the "Source" group, and modify it to match the code shown in Example 4-4.
Example 4-4. Working with mutable arrays
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// a
// b
// c
// d
[array release];
// e
[pool release];
return 0;
}
The code we added in Example 4-4 performs the following tasks:
a. Creates a new mutable array
b. Adds an object to the array
c. Adds another object to the array
d. Prints the array
e. Releases the array, since we created it using the alloc method
3. Build and run (
-R) the application. You should see the following output in the console:
www.it-ebooks.info
We'll further explore the NSMutableArray class using the debugger:
1. Set a breakpoint before the line of code that releases the array (line 10).
2. Build and debug (
-Y) the application. Execution will start and then pause at the breakpoint.
www.it-ebooks.info
New Project
Tool
3. Add the Address Book framework to the project by selecting the Project
Add Frameworks menu item. A dialog box
will open, asking you to select the framework to add. It should open up to the /System/Library/Frameworks folder. If not,
navigate to that folder, and select the AddressBook.framework folder to add to the project. After you click the Add button, a
sheet will appear to control how the framework should be added. The settings shown will be fine, and all you need to do is
click the Add button again.
This step ensures that Project Builder links against the AddressBook framework, as well as the Foundation framework, when
it builds our application.
4. Open the main.m file, and modify it to match the following code:
#import <Foundation/Foundation.h>
#import <AddressBook/AddressBook.h>
// a
// b
// c
// d
// e
// f
// g
www.it-ebooks.info
a. Imports the AddressBook API set. Without this line, the compiler cannot compile the main.m file, because it won't be
able to find the definitions for the Address Book classes.
b. Obtains the Address Book for the logged-in user.
c. Obtains an array containing all of the people in the Address Book.
d. Loops through the people to obtain an ABPerson object. The ABPerson class provides the methods to work with
the various attributes that a person record has in the Address Book database.
e. Gets the first name of the person.
f. Gets the last name of the person.
g. Prints the name of the person out to the console.
For more information about the various classes in the AddressBook framework, see the files in /
Developer/Documentation/AdditionalTechnologies/AddressBook.
5. Build and run ( -R) the application. You should see a list of your contacts output in the console. Here's a sample from our
run of the application, using the contacts pictured in Figure 4-4:
Davidson James Duncan
Hunter Jason
Ronconi Eleo
Horwat Justyna
Driscoll Jim
Davidson Ted
Branham Christine
Behlendorf Brian
O'Reilly Tim
Toporek Chuck
Czigany Susan
We haven't gone into great detail on the use of the AddressBook, but just a little knowledge on arrays has already let you work with
this important user data. By the time you're done with this book, just think how dangerous you will be! But no matter how dangerous
you get, you should remember to use the Address Book API when you create an application that needs to keep track of contacts.
Also, you'll be able to build some pretty neat apps using this data. For example, I'm considering building an application that
automatically prints Christmas cards to send to all the contacts that I consider to be friends.
4.2.4 Sets
Sets-implemented by the NSSet and NSMutableSet classes-are an unordered collection of objects in which each object can
appear only once. A set can be used instead of an array when the order of elements in the collection is not important, but when
testing to see if an object is part of the set (usually referred to as "testing for membership"), speed is important. Testing to see if an
object is a member of a set is faster than testing against an array.
4.2.5 Dictionaries
Dictionaries-implemented in the NSDictionary class-store and retrieve objects using key-value pairs. Each key-value pair in a
dictionary is called an entry. The keys in a dictionary form a set; a key can be used only once in a dictionary. Although the key is
[2]
usually a string (an NSString object), most objects can be used as keys. To enable the retrieval of a value at a later time, the key
of the key-value pair should be immutable or treated as immutable. If the key changes after being used to put a value in the
dictionary, the value might not be retrievable. The NSDictionary class provides the following methods to work with the contents
of an array:
- (unsigned)
www.it-ebooks.info
count
Returns the number of objects currently in the dictionary
- (id)
objectForKey:
(id)aKey
Returns the object that is indexed using the given key in the dictionary
- (NSArray *)
allKeys
Returns an array containing all of the keys in the dictionary
To practice working with dictionaries:
1. In Project Builder, create a new Foundation Tool (File
New Project
"dictionaries", and save it in your ~/LearningCocoa folder.
Tool
2. Open the main.m file, located in the "Source" group, and modify it to match the code shown in Example 4-5.
Example 4-5. Working with dictionaries
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray * keys =
[@"one two three four five" componentsSeparatedByString:@" "];
// a
NSArray * values =
[@"alpha bravo charlie delta echo" componentsSeparatedByString:@" "]; // b
NSDictionary * dict = [[NSDictionary alloc] initWithObjects:values
forKeys:keys];
printf("%s\n", [[dict description] UTF8String]);
[pool release];
return 0;
// c
// d
}
The code we added in Example 4-5 performs the following tasks:
a. Creates a new array based on a space-delimited string. This set of objects will serve as the keys for the dictionary.
b. Creates a new array that will serve as the values of the dictionary.
c. Creates a new dictionary with our keys and values.
d. Prints the dictionary, so it can be examined.
3. Build and run (
-R) the application. You should see output similar to the following appear in the console:
{five = echo; four = delta; one = alpha; three = charlie; two = bravo; }
4. This is a representation of the structure of the dictionary. Note that the elements are not stored in any particular order.
www.it-ebooks.info
Remember that the keys form a set in which uniqueness, not order, is critical.
We'll explore this example further using the debugger.
4. Set a breakpoint after the printf statement. If you typed in the code exactly as listed earlier, the breakpoint will be on line
15.
5. Build and debug (
-Y) the application, open the debugger console, and type the following:
www.it-ebooks.info
(id)aKey
Adds an entry to the dictionary, consisting of the given key-value pair. If the key already exists in the dictionary, the
previous object associated with that key is removed from the dictionary and replaced with the new object.
- (void)removeObjectForKey:
(id)aKey
Removes the key and its associated value from the dictionary.
Tool
www.it-ebooks.info
Example 4-6. Working with property lists
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray * array = [[NSMutableArray alloc] init];
[array addObject:@"San Francisco"];
[array addObject:@"Houston"];
[array addObject:@"Tulsa"];
[array addObject:@"Juneau"];
[array addObject:@"Pheonix"];
// a
// b
// c
NSString * plist =
[NSString stringWithContentsOfFile:@"cities.plist"];
printf("%s\n", [plist UTF8String]);
// d
// e
[array release];
// f
[pool release];
return 0;
}
The code we added inExample 4-6 does the following things:
a. Creates a new mutable array.
b. Adds a series of strings to the mutable array.
c. Writes the array to a file named cities.plist. Since this is not an absolute path, it will be written in the working
directory of application. In our case, this file will be written in ~/LearningCocoa/collectionfiles/build/cities.plist.
d. Creates a new string based on the contents of the file that we just wrote. Once again, we use a relative path.
e. Prints the contents of the file to the console.
f. Returns the array object that we created.
3. Build and run (
-R) the application. You should see output similar to the following appear in the console:
www.it-ebooks.info
Property Lists
Mac OS X uses property lists, frequently referred to as plists, to organize data into a form that is meaningfully
structured, easily transportable, and storable. Property list files are saved in an XML format for easy editing and
transportability.
You can see many examples of plist files in your ~/Library/Preferences folder. Property lists organize data into
named values and lists of values using several types directly represented as the following Cocoa objects: NSString,
NSNumber, Boolean, NSDate, NSData, NSArray, and NSDictionary. They make it easy for applications to
store preference data and, the case of the Info.plist file in an application bundle, communicate information about an
application to the system. To take a look at how applications can use plists to store configuration information, look at
the plist file for the menu bar clock.
1. Open the ~/Library/Preferences folder, and locate the com.apple.MenuBarClock.plist file.
2. Double-click on the file to open it with the Property List Editor application (located in the /Developer/
Applications folder).
The Property List Editor application can be used to browse the tree of properties. You can also hit the Dump button to
see the XML representation of the property list. Be careful not to save any edits you make, as you can severely
confuse an application by making the wrong changes here.
[1]
Objects placed into an array must implement certain methods to support this functionality. All of the Foundation classes that you are likely
to add to a collection are already prepared for this.
[2]
The object used as a key must respond to the isEqual: message and conform to the NSCopying protocol. Since we have not covered
protocols yet, the rule of thumb is that any Cocoa object provided in the Foundation framework can be used as a key. Other objects may not
work.
www.it-ebooks.info
www.it-ebooks.info
removed from memory when no other objects are interested in it, each object in Cocoa has
an associated reference count. When you allocate or copy an object, its reference count is
automatically set to 1. This indicates that the object is in use in one place. When you pass
the object to other objects, wanting to make sure the object stays around for their use, they
can use the retain method to increment the reference counter.
To visualize this, imagine that we have an object being held in three different arrays, as
shown in Figure 4-5. Each array retains the object to make sure that it remains available for
its use. Therefore, the object has a reference count of 3.
Figure 4-5. Reference counting
Whenever you are done with an object, you send a release message to decrement the
reference count. When the reference count reaches 0, the release method will invoke the
object's dealloc method that destroys the object. Figure 4-6 shows an object being
removed progressively from a set of arrays. When it is no longer needed, its retain count is
set to 0, and the object is deallocated.
Figure 4-6. Releasing of an object
www.it-ebooks.info
to other objects? Or, said another way, how do you release an object you would like to
return to the caller of a method? Once you return from a method, there's no way to go back
and release the object.
The answer is provided by the autorelease method built into the NSObject class, in
conjunction with the functionality of the NSAutoReleasePool class. The
autorelease method marks the receiver for later release by an
NSAutoreleasePool. This enables an object to live beyond the scope of the owning
object so that other objects can use it. This mechanism explains why you have seen dozens
of code examples that contain the following lines:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
...code...
[pool release];
Each application puts in place at least one autorelease pool (for each thread of control that
is running in the application) and can have many more. You put an object in the pool by
sending the object an autorelease message. In the case of an application's event cycle,
when code finishes executing and control returns to the application object, the application
object sends a release message to the autorelease pool, and the pool sends a release
message to each object it contains. Any object that reaches a reference count of 0
automatically deallocates itself.
When an object is used solely within the scope of a method that creates it, you can
deallocate it immediately by sending it a release message. Otherwise, use the
autorelease message for all objects you create and hand off to other objects so that
they can choose whether to retain them.
You shouldn't release objects that you receive from other objects, unless you have first
retained them for some reason. Doing so will cause their reference count to reach 0
prematurely, and the system will destroy the object, thinking that no other object depends
on it. When objects that do depend on the destroyed object try to access it, the application
will most likely crash. These kinds of bugs can be hard to track down, even though their
cause and fix are simple.
You can assume that a received object remains valid within the method in which it was
received and will remain valid for the event loop that is handling it. If you want to keep it
as an instance variable, you should send it a retain message and then autorelease it when
you are done using it.
www.it-ebooks.info
class's setter method might call it multiple times with the same object as an argument, the
order in which you release and retain the object references is important.
As a rule, you want to retain the new object before releasing the old one. This ensures that
everything works as anticipated, even if the new and old objects are the same. If you
reverse these steps, and if the new and old objects are actually the same, the object might
be removed permanently from memory before being retained.
Here is the retain, then release rule expressed in code:
- (void)setProperty:(id)newProperty
{
[newProperty retain];
[property release];
property = newProperty;
}
There are other ways to ensure connections in setter methods, many of which are valid and
appropriate for certain situations. However, this is the simplest possible pattern we can give
that will always work. We will use this pattern throughout the book.
www.it-ebooks.info
This explains why we haven't been releasing the strings created with the
@" . . . " construct.
If you apply these rules of thumb consistently and keep the retain counts of your objects
balanced, you can manage memory in your applications effectively.
www.it-ebooks.info
4.4 Exercises
1. Investigate the lowercase and uppercase methods of NSString using the
debugger.
2. Write a Foundation Tool command-line application that prints the contents of any
filename given to it.
3. Read the documentation on your hard drive about the NSArray, NSSet, and
NSDictionary classes.
4. Modify the arrays example application so that it saves the contents of the array to a
file.
5. Write an example that saves a dictionary to disk. Don't just use string objects in the
array, but use some other objects like dictionaries and numbers so that you can see
how Cocoa saves different types out to XML property lists.
6. Examine the code we've written so far with an eye for how memory is managed. (A
bug regarding memory management has been left in one of the examples.)
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
Windows
Nib files
Outlets
Actions
5.1.1 Windows
A window in Cocoa looks similar to windows in other user environments, such as
Microsoft Windows or earlier versions of the Mac OS. A window can be moved around the
screen and stacked on top of other windows like pieces of paper. A typical Cocoa window,
shown in Figure 5-1, has a titlebar, content area, and several control objects.
Figure 5-1. A typical Cocoa-based window
Many user-interface objects other than the standard windows are window objects without
the standard window widgets. These include menus, pop-up menus, dialog boxes, sheets,
alerts, panels, info windows, tool tips, tool palettes, and scrolling lists. In fact, anything
drawn on the screen must appear in a window. End users, however, may not recognize or
refer to them as "windows."
www.it-ebooks.info
www.it-ebooks.info
5.1.3 Outlets
An outlet is a special-instance variable, marked with the IBOutlet keyword in a class's
header, that contains a reference to another object, as shown in Figure 5-3. An object can
communicate with other objects in an application by sending messages to them through
outlets.
Figure 5-3. Object connected to a text field via an outlet
5.1.4 Actions
Actions are special methods, indicated with the IBAction keyword, which are defined by
a class and triggered by user-interface objects. Interface Builder recognizes action
declarations in a header file, as it does with outlets. Similarly, Interface Builder allows you
to connect actions that a user might take with an interface, such as pushing a button, to
methods on an object. These connections are shown in Figure 5-4.
Figure 5-4. Targets and actions
An action refers both to a message sent to an object when the user clicks a button (or
manipulates some other control), and to the invoked method.
www.it-ebooks.info
The MVC paradigm works well for many applications, because the controller's central and
mediating role frees the model objects from needing to know about the state and events of
www.it-ebooks.info
the user interface. Likewise, the view objects don't have to know about the programmatic
interfaces of the model objects. Dividing the problem along these lines helps encapsulate
the various objects in an application. This can also aid reuse, since the model could be used
elsewhere, perhaps even on another platform.
MVC, strictly observed, is not advisable in all circumstances. Sometimes it can be
advantageous to combine roles. For example, in an graphics-intensive application, such as
an arcade game, you might have several view objects that merge the roles of view and
model for performance reasons. In other applications, especially simple ones, you can
combine the roles of controller and model; these objects join the special data structures and
logic of model objects with the controller's hooks to the interface.
The Controller object will assume the central role in the application. Like all controller
objects, it communicates with the interface and model objects, and it handles tasks specific
to the application. The Controller object gets the values that users enter into fields,
passes these values to the Converter object, gets the result back from the Converter,
and puts this result into a field in the interface. By insulating the Converter from the
implementation-specific details of the user interface, the Converter object becomes a
reusable component for other applications.
www.it-ebooks.info
main.m
Currency Converter
Created by James Duncan Davidson on Fri Aug 30 2002.
Copyright (c) 2002 __MyCompanyName__. All rights reserved.
#import <Cocoa/Cocoa.h>
int main(int argc, const char *argv[])
{
return NSApplicationMain(argc, argv);
}
Figure 5-7. Creating a new Cocoa Application
www.it-ebooks.info
Notice that the import statement has changed to importing Cocoa.h instead of Foundation.
h. The Cocoa.h header contains the definitions for both the Foundation and AppKit classes.
Also notice that the main method makes a call to the NSApplicationMain function.
This function is defined by the AppKit and will start the application, load the main nib file,
and set up the event loop and autorelease pool for that loop. Now that we have taken a look
at this source file, we can let it be. You'll very rarely, if ever, modify the main.m file of a
Cocoa GUI application.
Project Builder automatically generates the comments at the top
of the source file from the Cocoa Application template. You'll
probably want to change the __MyCompanyName__ text to the
actual copyright holder and make sure that the copyright year is
correct. See Chapter 17 for more details on how to finish and
polish your applications.
www.it-ebooks.info
www.it-ebooks.info
A default menu bar and window, titled Window, will appear when the nib file is opened.
When you have opened the Info window, use the following process to resize the window:
1. Select Size from the Info window's pop-up menu.
2. In the Content Rect area, select Width/Height from the right-hand pop-up menu.
3. In the text fields under the Width/Height menu, type 400 in the width (w) field and
200 in the height (h) field, as shown in Figure 5-9.
www.it-ebooks.info
1. Select Attributes from the Info window's pop-up menu, and change the window's
title to Currency Converter, as shown in Figure 5-10.
2. Verify that the Visible at launch time option is selected. This will ensure that this
window is created on screen when the application is launched.
3. Deselect the Resize checkbox in the Controls area. This will prevent users from
resizing the application.
Figure 5-10. Info window showing the Attributes panel
www.it-ebooks.info
2. Resize the text field by grabbing a handle and dragging it in the direction in which
you want it to grow. In this case, drag the left handle to the left to enlarge the text
field, as shown in Figure 5-12.
Figure 5-12. Resizing a text field
Just as you can specify the size of the application window, you can also specify
exact sizes for other elements of your application. For example, if you want the text
field to be 150 pixels wide, select the NSTextField object, and then select Size
from the NSTextField Info window (Shift- -I). In the width field (w), enter 150
as the value, and hit the Tab key to accept the value; the NSTextField object will
conform to its newly defined dimensions.
www.it-ebooks.info
make it the same size, or you can duplicate the first object. To create a new text field by
duplication:
1. Select the text field, if it is not already selected.
Duplicate (or use the keyboard shortcut,
2. Choose Edit
field appears slightly offset from the original field.
Another way to duplicate a field is to click on the object, then hold down the Option
and drag the object. A plus sign will appear next to the pointer to indicate that you're
making a copy of the object, and the guidelines will help you move the newly
duplicated object into place.
3. Reposition the new text field under the first text field. You'll notice that the guides
will appear once again to help you move the second text field into place.
4. To make the third text field, make another duplicate. Notice that Interface Builder
remembers the offset from the previous Duplicate command and automatically uses
that offset to create and place the third text field.
www.it-ebooks.info
www.it-ebooks.info
As you type in the new labels, you'll notice that the text fields aren't wide enough to
hold the text shown in Figure 5-15. To correct this problem, resize the text fields by
grabbing the middle-left field holder and dragging the edge of the text field to the
left until all of the text appears.
www.it-ebooks.info
4. Align the button under the text fields. To center the button under the text fields, you
can pop up a set of measurement guides that tell you the distance from an object to
any of its neighboring objects. With the button selected, hold down the Option key
and point to an object whose distance from the button you want to see, as shown in
Figure 5-17. With the Option key still down, use the arrow keys to nudge the button
to the exact center of the text fields.
Figure 5-17. Aligning the Convert button with the text fields
www.it-ebooks.info
1. Move the third text field and label down a bit in the user interface.
2. Drag a horizontal line from the Views palette onto the interface, and use the
alignment guides to place it right under the dollars text field.
3. Use the selection handles on the line to extend it to each side of the interface.
4. Move the result text field and label back up into position using the guides, then
move the Convert button up into place.
5. Resize the window using the guides to give you the proper distance from the text
fields on the right and the Convert button on the bottom.
At this point, Currency Converter's application window should look like Figure 5-18.
Figure 5-18. The final Currency Converter interface
www.it-ebooks.info
Layout
Show Layout Rectangles ( -L). Also, Interface Builder's Size
Inspector has a pop-up to toggle between the frame and layout rectangle, so you
can set values by hand when appropriate.
Look in the Alignment and Guides submenus of the Layout menu for various
alignment commands and tools. You can also use the alignment tool (Tools
Alignment) to provide a floating window with buttons that perform various types
of alignment functions.
www.it-ebooks.info
Next, we want to ensure that when the user presses the Tab key, the focus moves to another
text field. To do this:
1. Select the first text field, and Control-drag a connection line from it to the second
text field, as shown in Figure 5-20.
Figure 5-20. Connecting text fields for inter-field tabbing
www.it-ebooks.info
2. Select the nextKeyView outlet in the Info window, and click the Connect button.
3. Repeat the previous two steps, but connect the second field to the first. This will
make it so you can tab from the second field back up to the first text field.
2. Choose File
can test it.
Test Interface (
3. Try various operations in the interface, such as tabbing, cutting, and pasting
between text fields.
4. When finished, choose Quit New Application (
application menu to exit the text mode.
Notice that the screen position of the Currency Converter window in Interface Builder is
used as the initial position for the window when the application is launched. Place the
window near the top-left corner of the screen so that it will be a convenient (and traditional)
initial location.
www.it-ebooks.info
www.it-ebooks.info
1. Select the Controller class in the Classes window, as shown in Figure 5-21.
2. Select the Attributes menu item in the Info window.
3. Add an outlet named rateField by clicking the Add button, entering the name,
and pressing Return.
4. Create three more outlets, named dollarField, totalField, and
converter, as detailed in step 3.
5.5.1.2 Define actions for the controller
The Controller class needs only one action method to respond to user-interface events.
When the user clicks the Convert button (or uses the Return key, which we defined as an
equivalent), we want a convert: message sent to an instance of the Controller.
1. Click on the Action tab in the Info window.
2. Add an action named "convert:". Interface Builder will add the ":" for you if
you don't.
-S
www.it-ebooks.info
www.it-ebooks.info
Figure 5-23. Connecting the Controller instance to the rate text field
2. Interface Builder will bring up the Connections display of the Info window. Select
the action that corresponds to the first field, rateField.
3. Click the Connect button.
4. Following the same steps, connect the Controller's dollarField and
totalField outlets to the appropriate text fields.
To tell the controller that it is time to perform an action, we need to hook up the Convert
button to the Controller.
1. Control-drag a connection from the Convert button to the Controller instance
in the nib file window. Instead of dragging from the controller object to an interface
object, we are dragging a connection from a user-interface object to the controller.
2. In the Connections Info window, make sure that the target is selected in the Outlets
column, as shown in Figure 5-24.
Figure 5-24. Connecting the Convert button to the Controller
www.it-ebooks.info
www.it-ebooks.info
Now, we leave Interface Builder for this application. You'll complete the application using Project Builder.
www.it-ebooks.info
Look at the Controller.h file that Interface Builder generated. Notice that in addition to being declared of type id, our
variables have an IBOutlet declaration. This is a macro that, in the compiler, doesn't evaluate anything. It is used as a
hint to Interface Builder's parser, telling it that the variable is an outlet. You will also notice that the convert: method
has a return type of IBAction. This type is the same as void and also tells Interface Builder that the method serves
as an action that can be hooked up to user-interface elements and other objects. These declarations allow you to add
outlets and actions in the code and enable Interface Builder to parse them. We'll see this in action in later chapters.
www.it-ebooks.info
3. Update the "empty" implementation of the convert: method in Controller.m that Interface Builder generated
for you, as shown in Example 5-3.
Example 5-3. Controller.m implementation file
#import "Controller.h"
#import "Converter.h"
// a
@implementation Controller
- (IBAction)convert:(id)sender
{
float rate = [rateField floatValue];
float amt = [dollarField floatValue];
float total = [converter convertAmount:amt atRate:rate];
[totalField setFloatValue:total];
}
//
//
//
//
b
c
d
e
@end
The lines we added do the following things:
a. Imports the Converter class interface.
b. Gets the value of the rateField outlet of the interface as a floating-point number. All text fields (and
other classes that inherit from NSControl) can present the data that they contain in various forms,
including doubles, floats, Strings, and integers.
c. Gets the value of the dollarField outlet of the interface as a floating-point number.
d. Calls the convertAmount:atRate: method of the Converter object instance.
e. Sets the value of the totalField outlet of the interface to the result obtained from the Converter
object instance.
www.it-ebooks.info
www.it-ebooks.info
5.9 Exercises
1. Change the font used by the text labels on the application to Helvetica.
2. Change the color of text displayed in the totalField to blue.
www.it-ebooks.info
www.it-ebooks.info
On the other hand, the window created by the window server is paired with an object
supplied by the AppKit-an instance of the NSWindow class. Each physical window in a
Cocoa program is managed by an instance of NSWindow or a subclass. As shown in
Figure 6-2, when an NSWindow object is created, the window server creates the physical
window being managed. The window server references the window by its window number
and the NSWindow object instance by its own identifier.
Figure 6-2. NSWindow objects and window server windows
www.it-ebooks.info
www.it-ebooks.info
NSResponder
NSResponder is an abstract class that enables event handling in all classes that
inherit from it. It defines the set of messages invoked when different mouse and
keyboard events occur. It also defines the mechanics of event processing among
objects in an application. We'll cover events in more depth in Chapter 8.
NSWindow
An NSWindow object manages each physical window on the screen. It draws the
window's frame area and responds to user actions that close, move, resize, and
otherwise manipulate the window. The main purpose of an NSWindow is to
display an application's user interface, or at least a part of it, in its content area. The
content area is that space below the titlebar and within the window frame.
NSWindow allows you to assign a custom object as its delegate to participate in its
activities. This allows you to add application-specific window functionality to your
application without requiring knowledge of the NSWindow class internals.
NSView
Any object you see in a widow's content area is an instance of a subclass of the
NSView class. Each view owns a rectangular region associated with a particular
window. A view produces the image content for that region and responds to events
occurring within it.
Graphically, a view can be regarded as a framed canvas. The frame locates the view
in its superview, defines its size, and clips the drawing to its edges. The frame can
be moved around resized, and rotated in the superview. Within the frame is the
bounds of the view-the rectangle within which the view draws itself.
Views draw themselves as an indirect result of receiving the display message or
one if its variants. This message leads to the invocation of a view's drawRect:
method and the drawRect: methods of all subviews of that view. The
drawRect: method should contain all the code needed to redraw the view
completely.
NSApplication
Every application has exactly one NSApplication object instance to supervise
and coordinate the overall behavior of the application. This object dispatches events
to the appropriate windows, which, in turn, distribute them onto their views. The
application object manages its windows; it also detects and handles changes in their
status, as well as its own active and inactive status. The application object is
www.it-ebooks.info
www.it-ebooks.info
6.1.4 Panels
A panel is a special kind of window that usually serves some auxiliary function in an
application. For example, much of the functionality of Interface Builder, such as the view's
palette and inspector, is implemented using panels. To support the roles they typically play,
panels differ from windows in the following ways:
The user can close a panel that is the key window by pressing the Escape key (if the panel
has a close button).
www.it-ebooks.info
The core program framework provides several ways for your application to access the
participating objects, so you need not define outlets or instance variables for every object in
the hierarchy.
By sending the appropriate message to the NSApp global variable, you can obtain
the application's NSWindow objects.
You can get the content view of a window by sending it the contentView
message. From the returned NSView object, you can get all subviews of the view.
You can obtain from an NSView instance most of the objects that it references. For
example, you can discover its window, its superview, and its subviews.
The relationship between these parts of an application's view hierarchy is shown in Figure
6-6.
Figure 6-6. Hierarchical relationship between major view hierarchy components
www.it-ebooks.info
www.it-ebooks.info
Screen coordinates
Window coordinates
View coordinates
www.it-ebooks.info
The screen coordinate system has just one function: to position windows on the screen.
When your application creates a new window, it must specify the window's initial size and
location in screen coordinates.
It applies only to a particular window. Each window has its own coordinate system.
Its origin is at the lower-left corner of the window. If the window moves, the origin
and the entire coordinate system move with it.
www.it-ebooks.info
This set of coordinate systems has several implications that are important in the layout and
drawing of user-interface elements:
www.it-ebooks.info
Controls and cells lie behind the appearance and behavior of most user-interface objects in Cocoa,
including buttons, text fields, sliders, and browsers. Although they are quite different types of
objects, they interact closely. Controls are responsible for the following:
Displaying the control to the user
Accepting user events, such as clicking or typing
Sending actions to other objects in response to a user event
A control usually delegates the first two responsibilities to cells. Cells, which are subclass instances
of the NSCell class, let you display text or images in a view without the full overhead of an
NSView subclass. This allows for greater flexibility when creating a control, such as a spreadsheet
table, with many identical elements.
The controls that Cocoa provides fall into the categories listed in Table 6-1.
Description
Boxes
Group together other views, including controls, in an area that can have a
border and title
Browsers
www.it-ebooks.info
Buttons
Combo Boxes
Allow a user to enter a value either by entering it directly into a text field or
choosing it from a pop-up list of preselected values
Forms
Image Views
Matrices
Group cells that work together in various ways, such as radio buttons
Outline Views
Progress Indicators
Show that a lengthy task is underway and, optionally, can display how much
of that task is complete
Sliders
Steppers
Tab Views
Table Views
Text Fields
Text Views
Controls act as managers of their cells, telling them when and where to draw and notifying them
when a user event occurs in their areas. This division of labor, given the relative "weight" of cells
and controls, conserves memory and provides a great boost to application performance. For
example, a matrix of buttons can be implemented as a single control with many cells instead of as a
www.it-ebooks.info
You can create, set, and modify formatter objects programmatically or with Interface Builder.
Formatter objects handle the textual representation of the objects associated with the cells and
translate what is typed into a cell to the underlying object. You can attach a formatter object to a
cell in Interface Builder or use the setFormatter: method of NSCell to associate a formatter
with a cell programmatically.
www.it-ebooks.info
LearningCocoa folder.
6.4.2.1 Open the main nib file
Begin by opening the application's main nib file in Interface Builder:
1. In Project Builder's Groups & Files pane, click on the disclosure triangle next to Resources
to reveal the MainMenu.nib file.
2. Double-click on the nib file to open it in Interface Builder.
A default menu bar and window will appear when the nib file is opened.
6.4.2.2 Create the user interface
Set the size and initial location of the application's main window by resizing and moving the
window in Interface Builder
1. Move the window near the upper-left corner of the screen by dragging its titlebar.
2. Make the window smaller using the resize control at the bottom-right corner of the window,
as shown in Figure 6-11.
Figure 6-11. Cocoa window with resize control
www.it-ebooks.info
www.it-ebooks.info
3. In the Outlets tab, click the Add button, and add an outlet named textField (as shown in
Figure 6-14); enter the name, and press Return.
Figure 6-14. Adding an outlet
Instantiate MyController, or
When you instantiate a class (that is, create an instance of it), Interface Builder switches to the
Instances pane and highlights the new instance, as shown in Figure 6-15. The instance is named
after the class.
Figure 6-15. Instances pane showing a Controller object instance
www.it-ebooks.info
www.it-ebooks.info
2. Interface Builder brings up the Connections pane of the Show Info window, as shown in
Figure 6-17.
3. Select textField, and click the Connect button.
Figure 6-17. Connections pane of the Show Info window
www.it-ebooks.info
4. Verify that the checkboxes in the Create column next to the .h and .m files are selected.
5. Verify that the checkbox next to Simple Date is selected in the Insert into targets column.
6. Click on the Choose button.
7. Save the nib file (File
Save, or
-S).
Now that we've built the basic interface, we can leave Interface Builder and switch to Project
Builder to complete the application. Click on Project Builder's icon in the Dock to leave Interface
Builder.
6.4.2.9 Statically type the outlet
By default, outlet declarations are dynamically typed using the id keyword. You can use id as the
type for any object, meaning that the class of the object is determined at runtime. When you don't
need a dynamically typed object, you can-and should-statically type it as a pointer to an object. It
takes a little extra time, but it is good programming practice. Static typing also allows the compiler
www.it-ebooks.info
www.it-ebooks.info
Save, or
-S).
www.it-ebooks.info
2. While the text field is selected, bring up the Show Info window (Shiftalready.
3. In the Formatter pane of the Show Info window, specify the %c date format, as shown in
Figure 6-21.
Figure 6-21. Formatter pane
Save, or
-S).
Quit NewApplication, or
-Q).
www.it-ebooks.info
www.it-ebooks.info
The target/action relationship is typically defined using Interface Builder, in which you
select a target object for a control, along with a specific action message that will be sent to
the target. Target/action relationships can also be set (or modified) while an application is
running.
www.it-ebooks.info
www.it-ebooks.info
target using an outlet; not surprisingly, the outlet is named target. To make this
connection:
1. Click on the Instances tab in the MainMenu.nib window.
2. Control-drag a connection from the Refresh button to the MyController
instance in the MainMenu.nib window, as shown in Figure 6-26. When the instance
is outlined, release the mouse button.
Figure 6-26. Creating a connection between the Refresh button and the
controller
3. In the Connections pane of the Show Info window, make sure target is selected in
the Outlets column.
4. Select refresh: in the right column, as shown in Figure 6-27.
Figure 6-27. Connecting to the refresh: method
www.it-ebooks.info
Save, or
-S).
4. Interface Builder will warn you that the file MyController.h already exists. Click on
the Merge button to bring up the FileMerge tool, as shown in Figure 6-28. If you
don't see the window shown in Figure 6-28, look for the FileMerge icon on your
Dock, and click it to bring the FileMerge window to the top.
Figure 6-28. Merge tool in action
www.it-ebooks.info
5. The FileMerge tool consists of three panes. The left pane is the newly generated
file from Interface Builder, the right pane is the file in your project, and the bottom
pane is the result of the merge. We want to keep our edits that were statically typed
for the textField outlet. To do this, we select the #1 arrow, then "Choose right"
from the Actions pop-up at the bottom-right corner of the window.
6. Save the MyController.h file from FileMerge (File
then close the window.
-S) and
www.it-ebooks.info
Save, or
Save Merge, or
-S).
There are other ways of adding outlets and actions to your source code and the nib files that
don't involve using the FileMerge tool. We'll see some of these other ways in later
chapters.
Another way to clarify connections is to consider who needs to find whom. With
outlets, the object needs to find some other object, so the connection is from the
www.it-ebooks.info
first object to the second object. With actions, the control object needs to find an
object to send messages to, so the connection is from the control object to the
target object.
Save, or
-S).
-R).
When the application launches, you can refresh the date display by pressing the Refresh
button. Of course, the date won't change if you've selected to show only the date in the text
field. If you've opted to also display the current time, hitting the Refresh button should
update the time.
As we progress through the chapters in this book, our examples
will contain more and more methods. It doesn't matter to the
compiler which order methods appear in your source files; they
can be in any order you want.
www.it-ebooks.info
www.it-ebooks.info
6.6 Exercises
1. Read the online documentation for the NSWindow and NSView classes.
2. Give the window of our Simple Date application a title other than "Window".
3. Go back to the Currency Converter application in Chapter 5, and statically type the
rateField, dollarField, and totalField outlets.
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
4. Generate the source files for MyView from the Classes menu (Classes
Create Files for MyView). (You can
also Control-click on MyView and select Create Files for MyView from the Context menu.)
5. Interface Builder then displays a dialog box.
6. Verify that the checkboxes next to MyView.h and MyView.m are selected in the Create column.
www.it-ebooks.info
7. Verify that the checkbox next to Red Square is selected in the Target column.
8. Click the Choose button to create the files.
2. With the CustomView object still selected, choose the Custom Class pane of the Show Info window (Tools
Show Info, or Shift- -I), and select the MyView custom class. The name of the view will change to MyView to
confirm this change, as shown in Figure 7-3.
Figure 7-3. Setting the class for the custom view
The nib now has enough information to create an instance of the MyView class and to assign it to an area of the window.
Save, or -S), and return to Project Builder by clicking on its icon in the Dock.
Save the nib file (File
www.it-ebooks.info
// a
// c
Save, or
-S).
Build and Run, or
www.it-ebooks.info
Our view looks like it works just fine. However, we have a slight problem. Resize the window and observe how the red
square is anchored to the bottom-left corner of the window and moves down as the window is stretched and resized.
Ideally, we'd like to have the square fill the window no matter how the user resizes it.
5. Quit the Red Square application (
Why does the view stay anchored to the lower left-hand corner of the window? The answer
lies in the fact that Cocoa's coordinate system starts at the lower-left corner. This behavior
takes programmers who are used to a coordinate systembased on the upper-right corner (such
as Carbon-based applications) a bit of time to adjust to.
www.it-ebooks.info
Setting the Autosizing to these settings means that the view will grow and shrink as necessary to keep the distance from
the edges of its parent view (the content view of the window) constant. Think of the springs as making the inside of the
view "springy" so that it can stretch in size, while the straight lines ensure that the distance between the view and the edge
of its container remains constant. If you wanted the view to remain a constant size in the middle of the window's content
view, you could turn the straight lines to springs and vice versa.
4. Save the nib file (
-S).
5. Switch back to Project Builder, and build and run ( -R) the project. Now when you run the program, you can
resize the window to any size you want, and the red square expands or contracts as needed.
At this point, we're done with the Red Square application. Close the project in both Interface Builder and Project Builder
before moving on to the next section.
Device RGB, where the primary components are red, green, and blue as generated by a device. Since
monitors, printers, and other output devices operate differently, colors defined in this space will vary
from device to device.
Calibrated RGB, where the primary components are red, green, and blue in a consistent, abstract
color space that can be translated to display with the same approximate colors on any output device.
This is the recommended color space for most work.
Calibrated HSB, where the primary components are hue, saturation, and brightness.
CMYK, where the primary components are cyan, magenta, yellow, and key (or black). This color
space is used by traditional four-color printing presses.
Grayscale, where the primary component is white.
To get a color that is defined by one of these color spaces, the NSColor provides the following class
methods:
+ colorWithDeviceRed:green:blue:alpha:
www.it-ebooks.info
+
+
+
+
colorWithCalibratedRed:green:blue:alpha:
colorWithCalibratedHue:saturation:brightness:alpha:
colorWithDeviceCyan:magenta:yellow:black:alpha:
colorWithCalibratedWhite:alpha:
Each of these methods takes float values for the various components between 0.0 and 1.0. For example, to
create a yellow color, you would use the following message on NSColor:
NSColor yellow = [NSColor colorWithCalibratedRed:1.0
green:1.0
blue:0.0
alpha:1.0];
www.it-ebooks.info
To produce proper results, all drawing code invoked by a view must be bracketed by
invocations of these methods. The display method, and its variants, of the NSView
class perform these duties automatically, so don't worry about locking focus in the
drawRect method. However, if you define some methods that need to draw in a view
without going through the display methods, you must first send a lockFocus message
to the view in which you are drawing before performing any drawing; then you can send
the unlockFocus message as soon as you are done.
Only one view at a time can have focus. If focus is already locked onto another view when
the lockFocus method is invoked, the previous view's lock is put onto a stack, so focus
can be restored to it when the lock of the current view is released with the unlockFocus
message.
www.it-ebooks.info
[hello drawAtPoint:NSMakePoint((bounds.size.width/2),
(bounds.size.height/2))
withAttributes:attribs];
// f
@end
www.it-ebooks.info
The code we added implements the same drawRect: method that was overridden in the Red Square application
(Example 7-1) and does the following things:
a. Gets an NSRect structure containing the bounds of the view.
b. Initializes an NSString containing the string that we want to draw into the view.
c. Creates an empty dictionary object (a Cocoa collection object like those we covered in Chapter 4) that will
be needed for the drawAtPoint:withAttributes: method in line f.
d. Sets the active drawing color to white.
e. Calls the NSRectFill function to fill the view. This will paint the entire view white.
f. Calls the drawAtPoint:withAttributes: method on the hello string. This draws the string at a
point that is half the width and half the height of the view. We give this method an empty attributes
argument to tell the system not do anything special when the string is drawn.
2. Save the project (
-S).
-R). You should see the string drawn in the window, as shown in Figure 7-6.
Figure 7-6. A Hello World string drawn into a view
Note that the string isn't perfectly centered in the view. This is because the drawing point that the string uses to
draw itself onto the view is at the lower-left hand corner of the bounding box of the string. This follows the same
logic as the screen, window, and view coordinate systems.
4. Quit the String View application (
-Q).
www.it-ebooks.info
// a
// b
-R) the application. You should see the string drawn into the window with our attributes, as seen
We're now done with the String View application. Close the project in both Project Builder and Interface Builder before
moving on.
www.it-ebooks.info
1. Open the MyView.m implementation file in Project Builder, and edit it to match Example 7-4.
Example 7-4. Drawing a string into a view
#import "MyView.h"
@implementation MyView
- (void)drawRect:(NSRect)rect
{
NSRect bounds = [self bounds];
NSPoint
NSPoint
NSPoint
NSPoint
//
//
//
//
a
b
c
d
www.it-ebooks.info
[NSBezierPath fillRect:bounds];
// e
// f
// g
// h
}
@end
The code we added in Example 7-4 does the following things:
a. Creates an NSPoint halfway along the bottom of the view
b. Creates an NSPoint halfway along the top of the view
c. Creates an NSPoint halfway up the left side of the view
d. Creates an NSPoint halfway up the right side of the view
e. Draws a path that encompasses the entire view and fills that path with the current drawing color (white)
f. Draws a path that encompasses the entire view and draws a line along that path in the current drawing color (black)
g. Draws a path from the NSPoint along the top of the view to the NSPoint along the bottom of the view
h. Draws a path from the NSPoint along the right side of the view to the NSPoint along the left side of the view
2. Save the project (
-S).
-R). You should see the lines drawn in the view as shown in Figure 7-8.
Figure 7-8. The Line View application in action
www.it-ebooks.info
#import "MyView.h"
@implementation MyView
- (void)drawRect:(NSRect)rect
{
NSRect bounds = [self bounds];
NSPoint bottom = NSMakePoint((bounds.size.width/2.0), 0);
NSPoint top = NSMakePoint((bounds.size.width/2.0), bounds.size.height);
NSPoint left = NSMakePoint(0, (bounds.size.height/2.0));
NSPoint right = NSMakePoint(bounds.size.width, (bounds.size.height/2.0));
[[NSColor whiteColor] set];
[NSBezierPath fillRect:bounds];
[[NSColor blackColor] set];
[NSBezierPath strokeRect:bounds];
[NSBezierPath strokeLineFromPoint:top toPoint:bottom];
[NSBezierPath strokeLineFromPoint:right toPoint:left];
[[NSBezierPath bezierPathWithOvalInRect:bounds] stroke];
}
@end
The single line of code creates a oval path the size of the bounds of the view, then draws it using the stroke method. Save the
project ( -S), then build and run the application ( -R). You should see something that looks like Figure 7-9.
Figure 7-9. Line View with an oval path drawn
www.it-ebooks.info
7.6 Exercises
1. Define the red color in Red Square by using the colorWithCalibratedRed:
green:blue:alpha: method.
2. Draw the string from String View into the Line View project, noticing where the
string is drawn.
3. Vary the width of the lines drawn by using the setDefaultLineWidth:
method of NSBezierPath.
4. Use Project Builder's "Find" feature to look up occurrences of NSBezierPath in
your project; then use it to find the occurrences of NSBezierPath in the AppKit
headers.
www.it-ebooks.info
This chapter focuses on events-both user- and program-generated-and how you intercept,
handle, and coordinate them in Cocoa.
www.it-ebooks.info
8.1 Events
Events in Cocoa are represented by instances of the NSEvent class. An event object can
be queried to discover its window, the location of the event within the window, and the
time the event occurred (relative to system startup). You can also find out which, if any,
modified keys (such as Command, Shift, Option, and Control) were pressed. An event also
contains the type of event it represents. There are many event types, falling primarily into
the following categories:
Keyboard events
Generated when a key is pressed or released or when a modified key changes. Of
these event types, key-down events are the most useful. When you handle a keydown event, you can determine the character or characters associated with the event
by calling the characters method on the event object.
Mouse events
Generated by changes in the state of the mouse button (down and up) and during
mouse dragging. Optionally, mouse events can also be generated when the mouse
moves without any button depressed.
Tracking-rectangle events
Generated by the window server when the mouse enters or exits a
programmatically set area (a tracking rectangle) in a window. This lets an
application change either the cursor or the content that the mouse is currently over.
For example, when you move the mouse over the window control buttons, you
generate events triggered by the mouse entering and exiting a rectangular region
around the control. This lets the control highlight itself. Also, an application usually
changes the cursor to an I-beam when the mouse is moved over editable text.
Periodic events
Generated by timers to notify an application that a certain time interval has elapsed.
An application can request that periodic events be placed in its event queue at a
certain event frequency. This is useful for applications that want to perform some
task at regular intervals, such as updating the frames of an animation or checking
email every few minutes.
www.it-ebooks.info
Every application has a central object named NSApp, which is an instance of the
NSApplication class. At the core of its responsibilities is the management of the run
loop . A run loop monitors the various sources of events and decides which object is
responsible for handling each event. It then sends a message, passing to the object an
NSEvent object instance to describe the particulars of the event. The event message
passes from NSApp to the appropriate window, to a view (commonly a control) within the
window, and eventually to the target object.
This is how a button "knows" that it has been clicked. The application forwards mouse
click events to it. The button object can then either process the event directly or, more
commonly, pass it on to a custom object that you define through the target/action pattern or
delegation. When the handling objects are finished responding to the message, control
unwinds and returns to NSApp, where the run loop processes the next event.
This cycle, also known as the event cycle, usually starts at launch time when the application
sends a stream of Quartz commands to the window server for it to draw the application
interface. The application then begins its main run loop and begins accepting input from
the user. When users click or drag the mouse or type on the keyboard, the window server
detects and processes these actions, passing them to the application as events.
Events sent to the application by the window server are placed on a queue in the order they
are received. On each cycle of the run loop, NSApp processes the topmost event in the
queue, as shown in Figure 8-2. When NSApp finishes processing the event, it gets the next
event from the queue and repeats the process again and again until the application
terminates.
Figure 8-2. The event queue
8.1.2 Responders
Recall that when we introduced the core program framework of NSWindow, NSView, and
NSApplication in Chapter 6, we mentioned that each of these classes inherits
functionality from the NSResponder class, as shown in Figure 8-3.
www.it-ebooks.info
The NSResponder class defines the default message-handling behavior of all eventhandling objects in an application. The responder model is built around the following three
concepts:
Event messages
Messages that correspond directly to an input event, such as a mouse click or a key
press.
Action messages
Messages describing a higher-level command to be performed, such as cut: or
paste:.
Responder chain
A series of responder objects to which an event or action message is applied. When
a given responder object doesn't handle a message, that message is passed to the
next object in the chain.
Responder chains allow responder objects to delegate responsibility to other objects in the
system. The series in a responder chain is determined by the interrelationships between the
application's view, window, and its NSApp object. For a view, the next responder is
usually its superview. The next responder of a window's content view is the window itself.
From there, the event is passed to the NSApp object.
For action messages, the responder chain is longer. Messages are first passed up the
responder chain to the window. Then, if the previous sequence occurred in the key
window, the same path is followed for the main window. After that, the message is passed
to the NSApp object.
www.it-ebooks.info
www.it-ebooks.info
Show Info).
3. Add an outlet named colorWell to DotView using the Info window, and set its type to NSColorWell
using the drop-down box in the Type column of the Outlet display, as shown in Figure 8-4.
Figure 8-4. Adding the colorWell and slider outlets to DotView
4. Add an outlet named slider to DotView, and set its type to NSSlider.
5. Add an action named setRadius: to DotView.
www.it-ebooks.info
Save, or
-S).
www.it-ebooks.info
-S) the nib file, and return to Project Builder, where we will finish the application.
// 1
// 2
// 3
www.it-ebooks.info
Now that we have defined the header, it's time to add the code for the implementation of the DotView class. The
code for this class is too long to fit nicely on one page, so we're going to approach this in two steps. First, Example 82 shows the skeleton of our class, with all the methods to implement shown in boldface. As you can see, you will be
asked to insert code from later examples as we build the Dot View application. The sections that follow will provide
the necessary code, along with explanations of what that code will do.
First, enter the boldface text from Example 8-2 into your DotView.m file, and insert the appropriate code from
Example 8-3 through Example 8-10 as you work through the following sections. A complete version of how your
DotView.m file should look is shown in Example 8-11.
Example 8-2. Skeleton code for DotView.m
#import "DotView.h"
@implementation DotView
- (id)initWithFrame:(NSRect)frame
{
// Insert code from Example 8-3
}
- (void)awakeFromNib
{
// Insert code from Example 8-4
}
- (void)dealloc
{
// Insert code from Example 8-5
}
- (void)drawRect:(NSRect)rect
{
// Insert code from Example 8-6
}
- (BOOL)isOpaque
{
// Insert code from Example 8-7
}
- (void)mouseDown:(NSEvent *)event
{
// Insert code from Example 8-8
}
- (IBAction)setColor:(id)sender
{
// Insert code from Example 8-9
}
- (IBAction)setRadius:(id)sender
{
www.it-ebooks.info
//
//
//
//
//
//
1
2
3
4
5
6
// 1
// 2
www.it-ebooks.info
1. Sets the color of the color well to the color set up in the initializer. In this case, the default color will be set to
red. (For reference, see line 5 in Example 8-3.)
2. Sets the initial value of the slider to the radius we defined in the initializer (line 4 from Example 8-3).
When this method completes, the application will be displayed to the user.
8.2.5.3 Implement the dealloc method
The dealloc method is called when the view is disposed of, giving it a chance to clean up its memory usage. Add
the dealloc method as shown in Example 8-5.
Example 8-5. DotView deallocation method
- (void)dealloc
{
[color release];
[super dealloc];
}
// 1
// 2
// 1
// 2
// 3
// 4
// 5
www.it-ebooks.info
// 6
}
The code performs the following tasks:
1. Declares an NSRect structure that defines the rectangle into which our dot will be drawn.
2. Sets the current drawing color to white (whiteColor) and draws the background of the view.
3. Determines the origin of the rectangle into which our dot will be drawn. Because we have to determine the
rectangle of the dot by specifying its origin rather than its center, and we want the center of the dot to be the
location of our center NSPoint, we have to offset the origin appropriately. This code finds a point offset
towards the origin of the view's coordinate system that will place the dot's center exactly where the user
clicked.
4. Defines the size of the rectangle into which our dot will be drawn. Since the code in step 3 determined the
lower-left corner of the rectangle, we simply need to size the rectangle to be the diameter (2 * radius) of
the dot.
5. Sets the current color of DotView to the active drawing color, based on the color we defined in line 5 of
Example 8-3.
6. Creates an oval Bezier path inside the dotRect rectangle and fills it with the default color.
8.2.5.5 Implement the isOpaque method
The Quartz graphics engine is designed to draw multiple layers of content quickly with various levels of
transparency. However, no matter how much performance the engineers at Apple manage to squeeze out of the code,
the Quartz engine can operate faster if it knows that a view doesn't need to be composited with its background. The
isOpaque method of NSView lets this optimization be performed. Add the isOpaque method as shown in
Example 8-7.
Example 8-7. Telling Cocoa that our view is opaque
- (BOOL)isOpaque
{
return YES;
}
If we return NO to this method, Quartz will composite anything drawn by our view with the contents of views behind
it. Since we return YES, Quartz doesn't need to perform this operation and can save a bit of time.
8.2.5.6 Implement the mouseDown: method
Overriding NSResponder methods in a view is the best way to handle events for the view. One such method is
mouseDown:, which is invoked when the user presses the mouse button. All of the NSResponder event-handling
methods receive an NSEvent object instance as an argument. This event contains the mouse location where the click
occurred in the coordinate system of the window.
Add an implementation of this method to match, as shown in Example 8-8.
www.it-ebooks.info
// 1
// 2
// 3
//
//
//
//
//
1
2
3
4
5
www.it-ebooks.info
// 1
// 2
www.it-ebooks.info
{
NSRect dotRect;
[[NSColor whiteColor] set];
NSRectFill([self bounds]);
dotRect.origin.x = center.x - radius;
dotRect.origin.y = center.y - radius;
dotRect.size.width = 2 * radius;
dotRect.size.height = 2 * radius;
[color set];
[[NSBezierPath bezierPathWithOvalInRect:dotRect] fill];
}
- (BOOL)isOpaque
{
return YES;
}
- (void)mouseDown:(NSEvent *)event
{
NSPoint eventLocation = [event locationInWindow];
center = [self convertPoint:eventLocation fromView:nil];
[self setNeedsDisplay:YES];
}
- (IBAction)setColor:(id)sender
{
NSColor * newColor = [sender color];
[newColor retain];
[color release];
color = newColor;
[self setNeedsDisplay:YES];
}
- (IBAction)setRadius:(id)sender
{
radius = [sender floatValue];
[self setNeedsDisplay:YES];
}
@end
Once your DotView.m file is complete, save the project (File
Save, or -S), and then build and run the Dot
Build and Run, or -R). You should see something that looks like Figure 8-6.
View application (Build
Figure 8-6. Drawing dots with the Dot View application
www.it-ebooks.info
Perform the following actions on the application to see if the code that we added works:
Click anywhere in the view, and you'll see the dot move to the point that you clicked. Each time you click the
mouse, a mouseDown event is sent to the view, resulting in the mouseDown: method being called.
Move the slider left and right, and watch the size of the dot get smaller or bigger, respectively. Notice that the
slider issues events to the application as it moves, allowing you to see the results of the action dynamically.
Click on the color well, and pick a new color for the dot from the palette that appears. Just like the slider, the
color well responds dynamically.
Note that when you resize the application, the view doesn't autosize as we'd probably like. Exercise 6 at the end of
this chapter (Section 8.5) adds this functionality.
www.it-ebooks.info
Some messages are purely informational, occurring after an event has happened. These allow a delegate to
coordinate its actions with the delegating object.
Some messages are sent before an action will occur, allowing the delegate to veto or permit the action.
Other delegation messages assign a specific task to a delegate, such as filling a browser with cells.
As an example, take a child who is told by a friend to act silly. Depending on the circumstances, he may or may not do
as his friend suggests. If his parents are around, the child might ask his parents (or at least glance at one of the parents
to see if they are looking) if he should act silly before doing so. In this case, the child is delegating the decision of
whether to act silly in front of his parents. The parent then has the opportunity to approve or deny the request to act
silly. Figure 8-7 shows this relationship, albeit abstractly.
Figure 8-7. Delegation in action
You can set a custom object as the delegate of a Cocoa framework object by making a connection in Interface Builder,
or you can set it programmatically by using the setDelegate: method. Your custom classes can also define their
own delegate variables and delegation protocols for client objects. Just remember that delegates are not retained by the
objects that delegate messages to them.
To show delegation in action, we will modify our Dot View application to respond to a request to close the
application's window. We will create and add a delegate that, when a window sends a windowShouldClose
message to it, will create an alert box asking the user if it is okay to close. To create the alert box, Cocoa provides the
NSRunAlertPanel function. This function has the following signature:
int NSRunAlertPanel (NSString
NSString
NSString
NSString
NSString
...)
*
*
*
*
*
title,
message,
defaultButtonLabel,
alternateButtonLabel,
otherButtonLabel,
// printf style args for message
Table 8-1 provides a brief summary of the parameters for this function.
www.it-ebooks.info
Description
title
The title of the sheet, displayed near the top of the sheet in a bold font.
Msg
An optional message string that appears near the bottom of the sheet. The string can contain
printf-style arguments such as %@, %s, and %i.
defaultButton
alternateButton The label for the sheet's alternate button, typically "Cancel".
otherButton
The label for a third button. If you pass nil, only two buttons will appear on the sheet.
Instantiate MyDelegate).
3. In the Instances pane of the MainMenu.nib window, Control-drag a connection from the Window icon to the
MyDelegate object icon, as shown in Figure 8-8.
Figure 8-8. Control-dragging from Window to MyDelegate
www.it-ebooks.info
1. Make the connection to the delegate outlet of the window by clicking on the Connect button in the Info
window.
2. Save the nib file (
-S).
3. Create the files for the MyDelegate class, and add them to the project.
a. Click on the Classes tab in the MainMenu.nib window.
b. Select MyDelegate.
c. Select Classes
Return to Project Builder, and edit MyDelegate.m to match the code in Example 8-12.
Example 8-12. MyDelegate.m
#import "MyDelegate.h"
@implementation MyDelegate
- (BOOL)windowShouldClose:(NSWindow *)sender
{
int answer = NSRunAlertPanel(@"Close", @"Are you certain?",
@"Close", @"Cancel", nil);
switch (answer) {
case NSAlertDefaultReturn:
return YES;
default:
return NO;
}
}
// 1
// 2
// 3
@end
The code we added performs the following tasks:
1. Implements the windowShouldClose: method. When a window has a delegate that implements this
method, it asks the delegate if it should close before doing so.
2. Calls the NSRunAlertPanel function, which will open an alert dialog box that asks the user's permission to
close the window.
3. Returns YES or NO depending on the result from the alert dialog box. If the alert dialog box returns a value that
matches the constant NSAlertDefaultReturn, then the window will be closed.
Now save the project ( -S), and build and run the application ( -R). When you try to close the application window,
you will see something that looks like Figure 8-9. Notice what happens when you hit the Cancel button on the alert
box. Notice that the alert only comes up when you close the window. If you quit the application, the alert panel will
not appear. A different delegate method, the applicationShouldTerminate: method of the
NSApplication class, is needed to enable this functionality. Exercise 4 at the end of the chapter (Section 8.5) will
do so.
Figure 8-9. Dot View asking permission to close
www.it-ebooks.info
www.it-ebooks.info
Parameter
Description
title
The title of the sheet, displayed near the top of the sheet in a bold font.
defaultButton
alternateButton
otherButton
The label for a third button. If you pass nil, only two buttons will appear on the sheet.
docWindow
modalDelegate
A reference to the object that will respond when the user dismisses the sheet.
didEndSelector
contextInfo
msg
An optional message string that appears near the bottom of the sheet. The string can
contain printf-style arguments such as %@, %s, and %i.
Edit the MyDelegate.m code, replacing the windowShouldClose: method and adding the sheetClosed:
method, as shown in Example 8-13.
Example 8-13. Changing MyDelegate.m to use sheets
#import "MyDelegate.h"
@implementation MyDelegate
- (BOOL)windowShouldClose:(NSWindow *)sender
{
NSString * msg = @"Should this window close?";
SEL sel = @selector(sheetClosed:returnCode:contextInfo:);
NSBeginAlertSheet(@"Close",
@"OK",
@"Cancel",
nil,
sender,
//
//
//
//
//
title
default label
alternate button label
other button label
document window
// 1
// 2
// 3
www.it-ebooks.info
self,
sel,
NULL,
sender,
msg,
nil);
//
//
//
//
//
//
modal delegate
selector to method
dismiss selector
context info
message
params for msg string
return NO;
// 4
}
- (void)sheetClosed:(NSWindow *)sheet
returnCode:(int)returnCode
contextInfo:(void *)contextInfo
{
if (returnCode == NSAlertDefaultReturn) {
[(NSWindow *)contextInfo close];
}
}
// 5
@end
This code performs the following tasks:
1. Creates a string that will be displayed in the sheet.
2. Obtains a selector to the method that calls the sheet back when finished. In this case, we want the
sheetClosed:returnCode:contextInfo: method (which we define in step 5) called.
3. Calls the NSBeginAlertSheet function with a whole set of arguments describing what the sheet should
display and what object and methods it should call when it is done.
4. Returns NO so that the application's run loop can continue. The window will remain open, but a sheet will be
attached to it.
5. Checks the value returned using the equality (==) operator from the sheet - which checks to see if two values
are equal to each other - and closes the window or not, depending on its value.
Too often, newcomers to C (and many not-so-newcomers) make the error of using the
assignment operator (=) when they mean to use the equality (==) operator. Using the
assignment operator in a check like this will usually result in an expression that is legal,
but will not work as expected. Such errors can be subtle and hard to catch.
Save the project ( -S), and then build and run the application (
W), you should see something like Figure 8-10.
www.it-ebooks.info
www.it-ebooks.info
8.4 Notifications
Another way to communicate events between objects in Cocoa is via a notification. A notification is a message broadcast to all
objects in an application that are interested in the event that the notification represents.
Notifications can also pass along relevant data about the event. Notifications differ from delegation in that notification happens
after the object has performed the action instead of before. The object receiving a notification doesn't get a chance to say
whether or not an action will be taken. Also, an object can have many notification observers, but only one delegate.
Using our child/friend/parent example again, once the child has acted silly, there might be a set of friends who will want to
know about it. Through notification, our child can tell his friends that he acted silly, as shown in Figure 8-11.
Figure 8-11. Notification
It would be impractical for everyone who wanted to know that the child had acted silly to register her interest directly with the
child. The child would have to implement the functionality needed to keep track of all the interested friends and send
notifications to them in turn. Luckily, Cocoa has provided a set of classes to help us with this. Here's the way the notification
process, shown in Figure 8-12, works in Cocoa:
1. Objects interested in an event that happens elsewhere in an application-say, the addition of a record to a databaseregister themselves with a notification center (an instance of the NSNotificationCenter class) as observers of
that event. During the registration process, the observer specifies that a method should be invoked by the notification
center when the event occurs.
2. The object that adds the record to the database (or some such event) posts a notification (an instance of the
NSNotification class) to the notification center. The notification object contains a tag that identifies the
notification, the ID of the object posting the notification, and, optionally, a dictionary of supplemental data.
3. The notification center then sends a message to each registered observer, invoking the method specified by each
observer and passing the notification.
Figure 8-12. Notifications
www.it-ebooks.info
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
APPKIT_EXTERN
NSString
NSString
NSString
NSString
NSString
NSString
NSString
NSString
NSString
NSString
NSString
NSString
NSString
NSString
NSString
NSString
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
NSWindowDidBecomeKeyNotification;
NSWindowDidBecomeMainNotification;
NSWindowDidChangeScreenNotification;
NSWindowDidDeminiaturizeNotification;
NSWindowDidExposeNotification;
NSWindowDidMiniaturizeNotification;
NSWindowDidMoveNotification;
NSWindowDidResignKeyNotification;
NSWindowDidResignMainNotification;
NSWindowDidResizeNotification;
NSWindowDidUpdateNotification;
NSWindowWillCloseNotification;
NSWindowWillMiniaturizeNotification;
NSWindowWillMoveNotification;
NSWindowWillBeginSheetNotification;
NSWindowDidEndSheetNotification;
An object that wants to receive one of these notifications must use the notification name when registering with the notification
center.
// 1
// 2
// 3
www.it-ebooks.info
www.it-ebooks.info
8.5 Exercises
1. Allow the dot in the Dot View application to follow a mouse drag so that the user
can interactively place the dot. Hint: use the mouseDragged: method defined in
the NSResponder class.
2. Set the initial position of the dot in the Dot View application to the center of the
view instead of the coordinates (50.0,50.0).
3. Optimize the setRadius: method so that setNeedsDisplay is only called if
the original value and the new value for radius variable are not the same.
4. Implement the NSApplication delegate method of
applicationShouldTerminate: to display a confirmation dialog box when
the user tries to quit the application.
5. Change the initial color of the dot from red to some other color.
6. Change the Dot View Application so the size of the view changes if the user resizes
(or maximizes) the window. What happens to the slider and color well when the
window is resized?
www.it-ebooks.info
www.it-ebooks.info
9.1 Protocols
Many pieces of Cocoa functionality make use of an Objective-C language feature called a
protocol. A protocol is simply a list of method declarations. A class is said to conform to a
protocol when it provides implementations of the methods that a protocol defines.
To help explain the concept of a protocol, think of the similarities between a waiter at a
[1]
restaurant and a vending machine. Even though the waiter and the vending machine are
nowhere close to being similar objects from an inheritance standpoint, they can both
implement methods related to serving food and taking money. Roughly, we could describe
a protocol implemented by these two objects as the following methods:
takeOrder
serveFood
takeMoney
returnChange
complainTo
Of course, a vending machine doesn't usually serve very tasty or nutritious food and doesn't
respond very well, if at all, to complaints. Additionally, you usually have to give vending
machines money before they will take your order. But let's not get caught up too much in
the details of our analogy. At a very basic level, the vending machine and waiter aren't all
that different from each other-at least from the point of view of the person getting food.
And note that it is easy to take this protocol and find other food-service situations in which
it applies, such as getting a donut from the local convenience store.
In object-oriented programming, protocols are useful in the following situations:
To declare methods that other classes are expected to implement. This lets
programs define methods that they will call on objects but that other developers
will actually implement, and this is crucial to loading bundles and plug-ins.
To declare the public interface to an object while concealing its class. This lets
more than one implementation of an object "hide" behind a protocol and prevents
users from using unadvertised methods.
To capture similarities among classes that are not hierarchically related. Classes
that are unrelated in most respects might need to implement similar methods for use
by similar outside components. Protocols help formalize these relationships while
preserving encapsulation.
Objective-C defines two kinds of protocols: informal and formal. An informal protocol
uses categories to list a group of methods but doesn't associate them with any particular
www.it-ebooks.info
class or implementation.
Categories
Categories are Objective-C constructs that allow you to add methods to existing
classes. This lets you add functionality to undefined classes in a way that is
different than inheritance. For example, the drawAtPoint: method that we
used on NSString in Chapter 7 is not defined in the Foundation Kit. Instead,
the AppKit defines it as a category that adds methods to the NSString class so
you can draw strings into views.
A detailed discussion of categories is beyond the scope of this book. For more
information on categories, see the Object-Oriented Programming and the
Objective-C Language book installed with the Developer Tools in the /
Developer/Documentation/Cocoa /ObjectiveC folder.
A formal protocol, on the other hand, binds the list methods to a type definition that allows
typing of objects by protocol name. Additionally, when a class declares that it implements
a formal protocol, all of the methods of the protocol must be implemented.
[1]
Duncan waited tables for many years while in college and is thankful that nobody tried to tip
him over in order to get more food or money out of him.
www.it-ebooks.info
accessor method or as instance variables. The key-value coding informal protocol (more accurately, the
NSKeyValueCoding protocol) is available for use by any object that inherits from NSObject. Several Cocoa components,
as well as its scripting support, take advantage of key-value coding.
The two basic methods for manipulating objects using the key-value coding protocol are as follows:
valueForKey:
Returns the value for the property identified by the key. The default implementation searches for a public accessor
method based on the key name given. For example, if the key name given is price, a method named price or
getPrice will be invoked. If neither method is found, the implementation will look for an instance variable named
price and access it directly.
takeValue:forKey:
Sets the value for the property identified by the key to the value given. For our example of price, the default
implementation will search for a public accessor method named setPrice. If the method is not found, it will attempt
to access the price instance variable directly.
Tool
Cocoa
Objective-C Class), as
www.it-ebooks.info
// a
// b
- (NSString *)name;
- (void)setName:(NSString *)aName;
// c
// d
- (NSNumber *)price;
- (void)setPrice:(NSNumber *)aPrice;
// e
// f
@end
This code adds the following things:
a. The name instance variable of type NSString. This variable will store the name of the food item.
b. The price instance variable of type NSNumber. This variable will store the price of the food item.
c. Accessor method that returns the name of the food item.
d. Accessor method that allows the name of the food item to be set.
e. Accessor method that returns the price of the food item.
f. Accessor method that allows the price of the foot item to be set.
4. Edit the FoodItem.m file as follows:
#import "FoodItem.h"
www.it-ebooks.info
@implementation FoodItem
- (id)init
{
[super init];
[self setName:@"New Item"];
[self setPrice:[NSNumber numberWithFloat:0.0]];
return self;
}
// a
- (NSString *)name
{
return name;
}
// b
- (void)setName:(NSString *)newName
{
[newName retain];
[name release]
name = newName;
}
// c
- (NSNumber *)price
{
return price;
}
// d
- (void)setPrice:(NSNumber *)newPrice
{
[newPrice retain];
[price release];
price = newPrice;
}
// e
@end
The code we added performs the following tasks:
a. Initializes the object with some default values.
b. Implements the name accessor method.
c. Implements the setName: accessor method. Notice that we retain the new object, release the old one, then set
the name variable to the new object, in accordance with the rules we discussed in Chapter 4.
d. Implements the price accessor method.
e. Implements the setPrice: accessor method.
5. Edit the main.m file (located in the Sources folder in Project Builder's left pane) as follows:
#import <Foundation/Foundation.h>
#import "FoodItem.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
FoodItem * candyBar = [[FoodItem alloc] init];
www.it-ebooks.info
// c
// d
// e
[pool release];
return 0;
}
The code that we added performs the following tasks:
a. Instructs the candyBar object to set the name instance variable to Aero.
[3]
b. Instructs the candyBar object to set the price instance variable to 1.25. We use the NSNumber class to
wrap primitive types for use as objects in collections and in key-value coding.
c. Instructs the candyBar object to return the object assigned to the name variable and prints it using the
NSLog function.
d. Instructs the candyBar object to return the object assigned to the price variable and prints it out using the
NSLog function.
e. Releases the candyBar object.
6. Save the project (
-R).
[3]
Aero is a very tasty chocolate bar made in Europe by Nestl. You can occasionally find them in the U.S. at specialty stores.
www.it-ebooks.info
In Model-Viewer-Controller (MVC) terms, a data source is a controller object that communicates with a model
object (typically an array) and the view object. This relationship is shown in Figure 9-3.
Figure 9-3. A data source as a controller between a model and a view
To have their data displayed properly, model objects must implement a couple of methods from the
NSTableDataSource informal protocol:
- (int)numberOfRowsInTableView:(NSTableView *)tableView;
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row;
The first method allows the table view to ask its data source how many rows of data are in the data model. The
second method is used by the table view to retrieve an object value from the data model by row and column.
www.it-ebooks.info
Show Info, or
2. Drag a table view object (NSTableView) from the Cocoa-Data views palette, as shown in Figure 9-4.
Figure 9-4. Adding a table view object to the interface
www.it-ebooks.info
www.it-ebooks.info
Don't confuse the Column Title field, located at the top of the Attributes panel,
with the Identifier field at the bottom of the window. These serve two entirely
different purposes. The Column Title field is for the benefit of your users and
should contain the title you've assigned to that column in steps 2 and 3. The
Identifier is an internal programmatic name that refers to the name of the property
that should be displayed in the column.
Instantiate MyDataSource).
3. Draw a connection from the table view object to the MyDataSource object in the Instances window. Make
sure that you have selected the table view, not its surrounding scroll view before you draw the connection.
The table view will turn a darker shade of gray when selected.
4. Select the dataSource outlet in the Connections pane of the Info window, as shown in Figure 9-7, and
click the Connect button.
Figure 9-7. Connecting the table view to MyDataSource
www.it-ebooks.info
5. Click on MyDataSource in the Classes tab of the MainMenu.nib window, and create the interface files
(Classes
Create Files for MyDataSource).
6. Save (
2. Open the MyDataSource.h file, and edit it to match the following code:
#import <Cocoa/Cocoa.h>
@interface MyDataSource : NSObject
{
NSMutableArray * items;
}
@end
The code we added in the header file simply declares a single array, named items, as an instance variable.
We will hold the many items to be displayed in the user interface of our application in this array.
3. Open the MyDataSource.m file, and edit it to match the following code:
#import "MyDataSource.h"
#import "FoodItem.h"
@implementation MyDataSource
- (id)init
{
www.it-ebooks.info
[super init];
// Some initial data for our interface
FoodItem * chimi = [[FoodItem alloc] init];
FoodItem * fajitas = [[FoodItem alloc] init];
[chimi setName:@"Chimichanga"];
[chimi setPrice:[NSNumber numberWithFloat:8.95]];
[fajitas setName:@"Steak Fajitas"];
[fajitas setPrice:[NSNumber numberWithFloat:10.95]];
items = [[NSMutableArray alloc] init];
[items addObject:chimi];
[items addObject:fajitas];
[chimi release];
[fajitas release];
return self;
// a
// b
}
- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
return [items count];
}
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(int)row
{
NSString * identifier = [tableColumn identifier];
FoodItem * item = [items objectAtIndex:row];
return [item valueForKey:identifier];
}
// c
// d
// e
// f
@end
The code that we added performs the following tasks:
a. Creates a couple of sample menu items and puts them into an NSMutableArray instance.
b. Releases the food items, now that they are stored safely in the array.
c. Returns the number of items in the food items array. This lets the table view know how many rows
contain data.
d. Gets the identifier of the column for which the table view wants data.
e. Obtains the food item that is at the specified index in the array.
f. Returns the value of the food item object that matches the property name of the identifier
obtained from the table column.
4. Save the project (File
Save, or -S), and then build and run the application (Build
or -R). You should see something like Figure 9-9.
Figure 9-9. The Menu application in action
www.it-ebooks.info
Play with the application a little bit: resize the window; resize the individual table columns; reorder the table columns
by dragging around the column headers. Quit the application ( -Q) when you are done.
// 1
// 2
// 3
www.it-ebooks.info
1. We're going to add a button to the interface. To enable a new row to be added when this button is pushed,
we'll need to add an action newButtonPressed: and an outlet table to MyDataSource. An easy way
to do this is to add the declarations yourself in the code. In Project Builder, edit the MyDataSource.h file to
match the following code. The code you need to add is shown in boldface.
#import <Cocoa/Cocoa.h>
@interface MyDataSource : NSObject
{
NSMutableArray * items;
IBOutlet NSTableView * table;
}
- (IBAction)newButtonPressed:(id)sender;
@end
2. Save the header file (File
Save, or
-S).
Read
3. In Interface Builder's Classes pane, select MyDataSource, and reload the source file (Classes
MyDataSource.h). This causes Interface Builder to reparse the header file and pick up the new outlet and
action.
4. Resize the table view to make room for a button.
5. Drag a button from the Cocoa-Views panel onto the interface, and change its name to New Item.
6. Select the table view, and reset its Autosizing attributes as shown in Figure 9-10.
Figure 9-10. Adding a button to Menu
7. Control-drag a connection between the MyDataSource object in the Instances tab of the MainMenu.nib
window and the table view. Connect it to the table outlet, as shown in Figure 9-11.
www.it-ebooks.info
8. Control-drag a connection between the New Item button and the MyDataSource object in the Instances tab.
Connect it to the newActionPressed: button, as shown in Figure 9-12.
Figure 9-12. Connecting the button to the data source
Save, or
www.it-ebooks.info
10. Edit the MyDataSource.m file, adding the newButtonPressed: method shown in the following code:
- (IBAction)newButtonPressed:(NSEvent *)event {
FoodItem * item = [[FoodItem alloc] init];
[items insertObject:item atIndex:0];
[item release];
[table reloadData];
[table selectRow:0 byExtendingSelection:NO];
// a
// b
// c
// d
}
The code we added performs the following tasks:
a. Creates a new item object.
b. Inserts the new item object into our data model array.
c. Instructs the table view to reload its data. This will cause the table view to call the
numberOfRowsInTableView: method again and load all the rows from the model.
d. Selects the row we just added into the table. This highlights the new row, so the user of the
application can edit it.
11. Save the project files (File
Save, or -S), and then build and run the application (Build
Build and
Run, or -R). When you press the New Item button, a new row should be created, as shown in Figure 9-13.
Figure 9-13. Adding a new entry to our application
To edit the new fields, simply click in either column, and enter a new food item and price.
www.it-ebooks.info
// a
// b
// c
// d
www.it-ebooks.info
}
The code we added performs the following tasks:
a. Decodes the next object from the coder's data stream and sets the name instance variable.
b. Decodes the next object from the coder's data stream and sets the price instance variable.
c. Encodes the name instance variable to the coder's data stream.
d. Encodes the price instance variable to the coder's data stream.
3. Open MyDataSource.h, and add the following two action methods:
#import <Cocoa/Cocoa.h>
@interface MyDataSource : NSObject
{
NSMutableArray * items;
IBOutlet NSTableView * table;
}
- (IBAction)newButtonPressed:(id)sender;
- (IBAction)save:(id)sender;
- (IBAction)open:(id)sender;
@end
4. Save the source files, and then open the MainMenu.nib file in Interface Builder.
5. Reparse the MyDataSource.h file in Interface Builder. To do this, click on the MyDataSource object in the Classes
tab, and then select the Classes
Read File MyDataSource.h menu option.
6. Click on the File menu of the MainMenu.nib - MainMenu window to reveal the menu options.
7. Control-drag a connection from the File
Open... menu item to the MyDataSource instance in the Instances
tab, as shown in Figure 9-14. Connect it to the open: target.
Figure 9-14. Connecting the Open... menu item to MyDataSource
www.it-ebooks.info
Save, or
10. Add the save: and open: methods to MyDataSource.m, as well as two helper methods, as shown here:
#import "MyDataSource.h"
#import "FoodItem.h"
@implementation MyDataSource
.
.
.
- (IBAction)save:(id)sender
{
NSSavePanel * savePanel = [NSSavePanel savePanel];
SEL sel = @selector(savePanelDidEnd:returnCode:contextInfo:);
[savePanel beginSheetForDirectory:@"~/Documents"
file:@"menu.items"
modalForWindow:[table window]
modalDelegate:self
didEndSelector:sel
contextInfo:nil];
}
- (void)savePanelDidEnd:(NSSavePanel *)sheet
returnCode:(int)returnCode
contextInfo:(void *)context
{
if (returnCode == NSOKButton) {
[NSArchiver archiveRootObject:items toFile:[sheet filename]];
}
}
- (IBAction)open:(id)sender
// a
// b
// c
// d
www.it-ebooks.info
{
NSOpenPanel * openPanel = [NSOpenPanel openPanel];
SEL sel = @selector(openPanelDidEnd:returnCode:contextInfo:);
[openPanel beginSheetForDirectory:@"~/Documents"
file:nil
types:nil
modalForWindow:[table window]
modalDelegate:self
didEndSelector:sel
contextInfo:nil];
// e
}
- (void)openPanelDidEnd:(NSOpenPanel *)sheet
returnCode:(int)returnCode
contextInfo:(void *)contextInfo
{
if (returnCode == NSOKButton) {
NSMutableArray * array;
// f
array = [NSUnarchiver unarchiveObjectWithFile:[sheet filename]];
[array retain];
[items release];
items = array;
[table reloadData];
}
}
@end
The code that we added does the following things:
a. Creates a new Save panel-Cocoa's standard user-interface widget for selecting where a file should be saved.
The way we use the Save panel uses delegation in a manner similar to the sheet we added to the Dot View
application (Chapter 8).
b. Obtains the selector for the callback method that the Save panel should use when the user has selected
the file to which data will be saved.
c. Instructs the Save panel to display itself as a sheet attached to the current window. MyDataSource doesn't
have a direct reference to the window to which the sheet should be attached, but since it does have a
reference to the table, we can simply ask the table for the window object.
d. Archives the items array to the given file if the callback method gets a status code indicating that the user
selected the file to which to save.
e. Creates a new Open panel-Cocoa's standard user-interface widget for selecting files to open. Open panels
work very much like save panels.
f. Unarchives an array object from the file selected by the user; this releases the old array assigned to the
items variable and assigns a retained instance of the new items.
11. Now save the project (File
or -R).
Save, or
Add a few items to your list of food items, then save ( -S), and you should see the save dialog sheet slide out from the
titlebar of the application window, as shown in Figure 9-15.
Figure 9-15. Saving our menu list
www.it-ebooks.info
Quit the application, restart it ( -R), and then open ( -O) the data file you just saved. All the changes you made should
show up. Make sure to quit ( -Q) the application when you are done.
www.it-ebooks.info
3. In the number-formatter inspector, set up the format to use the currency settings
shown in Figure 9-17.
Figure 9-17. The number formatter inspector
www.it-ebooks.info
www.it-ebooks.info
Save or
Open the FoodItem.m file, and add the following two methods to the file after the other methods:
- (NSComparisonResult)compareName:(FoodItem *) item
{
return [name compare:[item name]];
}
// a
- (NSComparisonResult)comparePrice:(FoodItem *) item
{
return [price compare:[item price]];
}
// b
www.it-ebooks.info
a. This method returns a comparison result by using the compare: method of the NSString class to
compare the name of the given object with the name of the current instance.
b. This method returns a comparison result by using the compare: method of the NSNumber class to
compare the price of the given object with the price of the current instance.
3. Open the MyDataSource.m file, and add the following method:
- (void)tableView:(NSTableView *)tableView
didClickTableColumn:(NSTableColumn *)tableColumn
{
NSString * identifier = [tableColumn identifier];
if ([identifier isEqualToString:@"name"]) {
[items sortUsingSelector:@selector(compareName:)];
} else {
[items sortUsingSelector:@selector(comparePrice:)];
}
[table reloadData];
}
// a
// b
// c
// d
-R). Add a few items to the Menu, and then sort by name,
www.it-ebooks.info
9.8 Exercises
1. Change the title of the left column from Item Name to Food Item.
2. Add the code necessary to display a confirmation dialog box when the user tries to
quit the application.
3. Examine the code in the Menu application for memory management problems.
www.it-ebooks.info
www.it-ebooks.info
Cocoa provides a multiple-document architecture, helping you take care of these tasks
easily. Using this architecture drastically simplifies the work developers must do to
implement a multidocument application. Once you understand how this architecture works,
you can have a multidocument application up and running in minutes.
This chapter begins with an overview of Cocoa's multiple-document architecture and then
presents an in-depth look at the classes that make up this architecture. The final part of the
chapter guides you through the process of creating a simple multiple-document text-editing
application.
www.it-ebooks.info
www.it-ebooks.info
open document). In turn, an NSDocument object creates and manages one or more
NSWindowController objects, one for each of the windows displayed for a document.
In addition to these three AppKit classes, the multiple-document architecture uses
information in the application's info property list (saved as Contents/Info.plist in the
application's bundle - we'll discuss bundles more in Chapter 13) to determine the types of
data with which the application can work. The information is stored in the property list as
an array of document types. Each document-type entry in the array includes the following
information:
Project Builder provides a simple user interface for creating and editing entries in an
application's document type array. Even though there's usually no need to modify the
property list directly, the document controller uses the information from the info property
list to do the following things:
Filter out inappropriate file types automatically, allowing users to select only files
that the application can handle when an open dialog box is presented
Instantiate the appropriate NSDocument subclass for a document's data type when
a document is opened
Provide other objects in the application that the data displayed in its window(s).
The document object must provide the data in any of the formats supported by the
application.
Load data into internal data structures and display it in windows. The document
object must accept the data in any format supported by the application.
Store document data in a file at a specified location in the filesystem.
Read document data stored in a file.
www.it-ebooks.info
With the assistance of its window controllers, a document-object instance manages the
display and capture of the data in its windows. The document-object instance associated
with the key window is made the first responder to action messages indicating that a user
wants to save, print, revert, or close a document. A fully implemented document object
knows how to track its edited status, print document data, and perform undo and redo
operations. As you'll see in the examples in this and later chapters, these behaviors aren't
provided completely by default, but the NSDocument class goes a long way to assist you
in implementing each.
For edited-status tracking, the NSDocument class provides an API for updating a
document change counter. For undo/redo operations, NSDocument creates an
NSUndoManager when one is requested, which responds appropriately to the Edit
Undo and Edit
Redo menu commands, updating the change counter when undo and
redo operations are invoked.
Every application that takes advantage of the AppKit's document-based application
architecture must create at least one subclass of NSDocument. The architecture requires
that you override some methods of the NSDocument class. These methods must be
implemented:
- (NSString *)windowNibName;
Called by the document controller to determine the name of the nib file that
contains the user interface to view and edit the document.
- (void)windowControllerDidLoadNib:(NSWindowController *)
aController;
Called once the window controller has loaded the nib file and all of the user
interface connections have been made. This provides an opportunity for any
initialization that needs to be performed.
- (NSData *)dataRepresentationOfType:(NSString *)aType;
Must be implemented to create and return document data of a supported type,
usually in preparation for writing that data to a file as an NSData object.
- (BOOL)loadDataRepresentation:(NSData *)data:(NSString *)
aType;
Must be implemented to convert an NSData object (that contains the document
data of a particular type) into the document's internal data structures so that the
document is ready to display its contents. The NSData object usually results from
www.it-ebooks.info
www.it-ebooks.info
New
newDocument:
Open
openDocument:
Save
saveDocument:
Save As
saveDocumentAs:
Save To
saveDocumentTo:
Save All
saveAllDocuments:
www.it-ebooks.info
Close
closeDocument:
Revert
revertDocumentToSaved:
printDocument:
Page Layout
runPageLayout:
www.it-ebooks.info
www.it-ebooks.info
New Project).
2. Select Cocoa Document-based Application from the application type dialog box, as shown in Figure 10-2.
Figure 10-2. Creating a document-based application
www.it-ebooks.info
3. Name the project "Simple Text Edit", and save it into your ~/LearningCocoa folder.
www.it-ebooks.info
www.it-ebooks.info
Save, or
-R).
New, or
Close, or
-W).
Quit, or
-Q).
Next, we'll implement the functionality needed to turn this skeleton into a full-blown text editor that allows us to save and
open text files.
www.it-ebooks.info
In this section, you'll define the look and feel of the application's document. Just modify the default nib file (created by Project
Builder's template) by adding a text view that will allow the user to view and edit text.
1. Open MyDocument.nib in Interface Builder, if it isn't already open.
2. Remove the default text object that says "Your document contents here."
3. Drag an NSTextView to the window from the Cocoa-Data views pane of the palette, as shown in Figure 10-5.
Figure 10-5. Dragging a text view onto the document window
4. Move and resize the text view so that it occupies the entire window, as shown in Figure 10-6.
Figure 10-6. Resizing and setting the attributes of the text view
5. With the text view selected, bring up the Size pane in the Inspector. Change the Autosizing options so that the view
will follow changes in the windows size.
6. Switch back to Project Builder, open MyDocument.h, and add a declaration for the text view's outlet by inserting the
boldface text shown in Example 10-2.
Example 10-2. Adding the textView outlet to the NSDocument subclass
#import <Cocoa/Cocoa.h>
@interface MyDocument : NSDocument
{
www.it-ebooks.info
-S) MyDocument.h.
8. Bring Interface Builder to the front, and drag MyDocument.h from Project Builder's Group & Files listing into the
Instances panel of Interface Builder's MyDocument.nib window. This gives Interface Builder the opportunity to parse
the outlet, so you can use it for connections.
9. In Interface Builder's Instances pane, Control-drag a connection from the File's Owner instance (this is a proxy for a
MyDocument instance) to the text view.
10. Connect the textView outlet to the view by clicking on the Connect button in the Info window.
Do not generate an instance of MyDocument to make this connection. The documentbased application framework makes an instance automatically, which is assigned to the
File's Owner object. At runtime, the File's Owner will be an instance of MyDocument.
11. Save (
Simple View
4. Modify the default document type entry. Rename DocumentType to Text, and replace the quoted question marks with
txt in the Extensions field and with TXT in the OS types field. Once you've entered this information, click on the
Change button.
Figure 10-7. Editing a document type
www.it-ebooks.info
These settings allow the document architecture to recognize .txt files as files that can be opened by our application, instructing
the system to use an instance of the MyDocument class to open those files. In addition, the system will allow only files saved
from a MyDocument instance to have the extension .txt.
www.it-ebooks.info
Clean),
[1]
Build and
a. Type some text into the running application. Use Cut and Paste to edit the text.
b. Save the document. Note the filename appears in the window's titlebar. Make sure that the "Hide Extension"
checkbox is not clicked so that you can see the extension of the file in the Finder and other applications.
www.it-ebooks.info
Close, or
-W).
e. Open the document you saved in step 2 in TextEdit (/Applications) to see how Mac OS X's default text editor
handles the data created by the Simple Text Editor application.
f. Quit TextEdit.
Cocoa's multiple-document architecture, as well as the capabilities built into the NSTextView class, provides the
functionality that users expect Cfrom a text editing application. We've simply glued these features together by adding just a
few lines of code.
[1]
A bug in Project Builder (up to and including version 2.0.1) requires you to clean the project so that the new Info.plist settings can
be incorporated into the application.
www.it-ebooks.info
10.3 Exercises
1. Read the Apple developer documentation on the NSDocumentController and
NSDocument classes.
2. Add the ability for the editor to read and write Property List (plist) files.
3. Try to revert (File
to work?
www.it-ebooks.info
www.it-ebooks.info
At the bottom layer, the NSTextStorage class gives programmatic access to the text.
This allows you to search for text and manipulate paragraph and character styles without
incurring the overhead of laying out the text for display.
In the middle layer are the NSLayoutManager and NSTextContainer classes, which
control the way text is laid out on-screen or on the printed page. An NSTextContainer
object defines a region where text can be laid out. Typically, this is a rectangular region,
but subclasses can support other shapes. If a container's area is inset from the text view's
bounds, a margin appears around the text. An instance of the NSLayoutManager class
displays the text contained in an NSTextStorage object, rendering text in an
NSTextView's display according to the areas defined by NSTextContainer objects.
For most uses, the API provided by the NSTextView class is all that you need to learn to
enable rich-text functionality in your applications.
www.it-ebooks.info
A simple string
As we saw in Chapter 10, you can set and read the contents of a text view to and
from an NSString instance. This is the easiest way to deal with plain text files.
Data as rich text
The Rich Text Format (RTF) is a standard created by Microsoft for representing
text with multiple fonts and colors. RTF is supported by many word processors
(including the TextEdit application that comes with Mac OS X) as an interchange
format, but serves quite well as a primary document format. Files saved in RTF
format will be assigned an .rtf extension.
Data as rich text with images
Standard RTF files can also contain attachments, such as images, audio clips, and
even QuickTime movies, which are embedded in the file. These files, known as
RTFD (the "D" stands for directory) use a type of package format, or directory, in
which the embedded files of the RTF document are stored. The package will
contain the RTF file (e.g., text.rtf), along with any associated attachments (e.g.,
fuzzball.tiff). When the file is saved, it will be assigned an .rtfd extension.
A directory wrapper that holds a directory and all the files and subdirectories within
it
A regular file wrapper that holds the contents of a single file
A link wrapper that represents a symbolic link, or alias, in the filesystem
The NSDocument class provides the following methods for loading data from, and saving
data to, file wrappers:
- (BOOL)loadFileWrapperRepresentation:(NSFileWrapper *)
wrapper ofType:(NSString *)type
Loads the document data contained by the given file wrapper into the receiving
NSDocument object
www.it-ebooks.info
- (NSFileWrapper *)fileWrapperRepresentationOfType:(NSString
*)type
Returns a file wrapper object that represents the contents of the document for a
given document type
In fact, the NSDocument implementation of these methods actually calls the
dataRepresentationOfType: and loadDataRepresentation:ofType:
methods that we used in the Simple Text Edit application in Chapter 10. By overloading
the file wrapper load and save methods, we can support RTFD.
www.it-ebooks.info
New Project).
3. Name the project "RTF Edit", and save it into your ~/LearningCocoa folder.
4. Compose the UI by opening the MyDocument.nib file in Interface Builder and performing the following steps:
a. Remove the default text field.
b. Drag an NSTextView from the Views palette to the application's window.
c. Resize the text view so that it occupies the entire window.
d. Change the Autosizing options so that the view will follow changes in the window's size.
5. In Project Builder, open MyDocument.h from the Classes directory, and add a declaration for the text view's outlet.
#import <Cocoa/Cocoa.h>
@interface MyDocument : NSDocument
{
IBOutlet NSTextView * textView;
}
@end
6. Save (File
Save, or
-S) MyDocument.h, and then drag it onto Interface Builder's MyDocument.nib window so that
Interface Builder can pick up the change to the file.
7. Control-drag a connection from the File's Owner object (remember, this is a proxy for the MyDocument instance at
runtime) to the NSTextView we added in step 4, and connect it to the textView outlet.
8. Save (File
Save, or
10. Modify the default document type entry as shown in Figure 11-12. Simply click on the "DocumentType" entry to select it,
rename "DocumentType" to "Rich Text", enter the values (rtf for Extension and RTF for OS types) into the Document Type
Information area, and click Change to apply these settings.
11. Open MyDocument.h, and add the dataFromFile instance variable to hold the raw RTF data loaded from a file.
#import <Cocoa/Cocoa.h>
@interface MyDocument : NSDocument
{
www.it-ebooks.info
www.it-ebooks.info
{
NSRange range = NSMakeRange(0,[[textView string] length];
NSFileWrapper * wrapper = [[NSFileWrapper alloc]
initRegularFileWithContents:[textView RTFFromRange:range]];
return [wrapper autorelease];
}
15. Finally, implement the windowControllerDidLoadNib: method.
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
[super windowControllerDidLoadNib:aController];
if (rtfData) {
// a
[[textView textStorage]
replaceCharactersInRange:NSMakeRange(0, [[textView string] length]);
withAttributedString:rtfData];
[rtfData release];
}
[textView setAllowsUndo:YES];
// b
}
The code we added does the following things:
a. If there is RTF data waiting, it is loaded into the text view.
b. Sets the text view to allow for undo actions to be performed.
16. Save the project (File
Save, or
-S).
Build and Run, or -R) the application. You should see the text editor as shown in Figure 11-3.
17. Build and run (Build
You should be able to save and open rich-text files with it.
Figure 11-3. RTF Edit in action
www.it-ebooks.info
Notice that when you drop it, a Font Manager object is added to the nib. This is a
www.it-ebooks.info
reference to the Font Manager so that you can connect it to other objects in your
application if needed.
3. Save (File
Save, or
a. Type some text into the document, and select it. Open up the Font Panel (Font
Show Fonts, or -T), and change the font. Watch the selected text
change, similar to what is shown in Figure 11-5.
Figure 11-5. Using the Font Panel
5. From the Extras pop-up menu at the bottom of the Font Panel, select the Color item.
Change the text to a different color by selecting a color and clicking the Apply button.
6. Close the Color and Font panels by clicking on their close window buttons.
7. Save your file, and then open it in Text Edit to see your changes work in various
applications.
www.it-ebooks.info
Class of value
Default value
NSAttachmentAttributeName
NSTextAttachment
None
NSBackgroundColorAttributeName
NSColor
None
NSBaselineOffsetAttributeName
NSNumber (float)
0.0
NSFontAttributeName
NSFont
Helvetica, 12pt
NSForegroundColorAttributeName
NSColor
Black
NSKernAttributeName
NSNumber (float)
0.0
NSLigatureAttributeName
NSNumber (int)
NSLinkAttributeName
id
None
NSParagraphStyleName
NSParagraphStyle
NSSuperScriptAttributeName
NSNumber (int)
NSUnderlineStyleAttributeName
NSNumber (int)
None
In Table 11-1, we refer to NSNumber (int) and NSNumber (float). This means that the attribute should be set to an
NSNumber object that was created with the type specified.
www.it-ebooks.info
5. Control-drag a connection from the Analyze button to the File's Owner object, and connect the button to the
analyzeText: method.
6. Save (
7. Edit the MyDocument.m file, and add the analyzeText: method as shown.
- (IBAction)analyzeText:(id)sender
{
int count = 0;
int fontChanges = -1;
id lastAttribute = nil;
NSTextStorage * storage = [textView textStorage];
while (count < [storage length]) {
id attributeValue = [storage attribute:NSFontAttributeName
atIndex:count
effectiveRange:nil];
if (attributeValue != lastAttribute) {
//
//
//
//
a
b
c
d
// e
// f
www.it-ebooks.info
fontChanges++;
}
lastAttribute = attributeValue;
count++;
// g
// h
}
NSBeginAlertSheet(@"Analysis",
@"OK",
nil,
nil,
[textView window],
nil,
NULL,
NULL,
nil,
@"Font Changes %i",
fontChanges);
//
//
//
//
//
//
//
//
//
title
default button label
cancel button label
other button label
document window
modal delegate
selector to method
dismiss selector
context info
// i
}
The code we added performs the following tasks:
a. Sets up a counter to loop through all the characters in the document. This will allow us to examine the
characters and notice font changes as we loop through the document.
b. Sets up a counter that will be used to keep track of the number of font changes that are found in the document.
c. Acts as a holder for the text-attribute object that was examined during a previous iteration of our loop.
d. Gets a reference to the text-storage object behind the text view.
e. Sets up a loop that will continue until we have examined every character in the document. Each time the loop is
executed, the attribute value for the current character is obtained.
f. Checks to see if the font attribute of the current character is the same as the last character. If not, we record the
change in font.
g. Stores this font attribute so that we can compare it to the font attribute we'll see the next time through the loop.
h. Increments our counter.
i. Creates our message and displays it to the user on a sheet attached to the window. We can pass in nil and
NULL to most of the arguments since the sheet is for informative purposes only.
8. Save the project (File
Save, or
-S).
9. Build and run ( -R) the application. Type some text, change the fonts, and then hit the Analyze button. You should
see something like Figure 11-7.
Figure 11-7. RTF Edit analyzing its text
www.it-ebooks.info
Our next set of additions to the code will change the formatting of the text in our document.
1. In Project Builder, edit the MyDocument.h file, and add the following action:
#import <Cocoa/Cocoa.h>
@interface MyDocument : NSDocument
{
IBOutlet NSTextView * textView;
NSData * dataFromFile;
}
- (IBAction)analyzeText:(id)sender;
- (IBAction)clearFormatting:(id)sender;
@end
2. Save the MyDocument.h file; then open MyDocument.nib in Interface Builder.
3. Reparse the MyDocument.h file in Interface Builder. To do this, drag the MyDocument.h file to the MyDocument.nib
window.
4. Add a button to our document interface, and name it Remove Formatting, as shown in Figure 11-8.
Figure 11-8. Adding a Remove Formatting button to RTF Edit
5. Control-drag a connection from the Remove Formatting button to the File's Owner object, and connect the button to
the clearFormatting: method.
www.it-ebooks.info
6. Save (
7. Edit the MyDocument.m file, and add the clearFormatting: method as shown.
- (IBAction)clearFormatting:(id)sender
{
NSTextStorage * storage = [textView textStorage];
NSRange range = NSMakeRange(0, [storage length]);
NSMutableDictionary * attribs = [NSMutableDictionary dictionary];
[attribs setObject:[NSFont fontWithName:@"Helvetica" size:12]
forKey:NSFontAttributeName];
[storage setAttributes:attribs range:range];
}
//
//
//
//
a
b
c
d
// e
Save, or
-S).
9. Build and run ( -R) the application. Type some text, change the fonts, and then hit the Remove Formatting button.
You should see all of your changes disappear.
// a
// b
www.it-ebooks.info
forKey:NSFontAttributeName];
[storage setAttributes:attribs range:range];
}
The code we added performs the following tasks:
a. Gets the undoManager from the document. Each document has an associated undoManager.
b. Registers an undo action with the undoManager. This action calls the underlying text-storage object's
setAttributedString: method with a copy of the current storage-effectively resetting the contents of
the storage to the same state as before the change.
2. Save the project (File
3. Build and run (
Save,
-S).
www.it-ebooks.info
3. Save (File
Save, or
www.it-ebooks.info
5. Type some text, and change the paragraph alignment using the four buttons along
the top-left hand side of the ruler.
6. Create another paragraph, and change its indentation settings using the controls
provided by the ruler.
www.it-ebooks.info
2. Add a new document type entry as shown in Figure 11-11. Fill out the Document Type Information fields with the fields
shown, and click Add. Don't forget to set the Document Class field to MyDocument.
Figure 11-11. Adding the rtfd file type to the RTF Edit application
www.it-ebooks.info
replaceCharactersInRange:NSMakeRange(0, [[textView string] length]);
withAttributedString:rtfData];
[rtfData
release];
}
return YES;
}
4. Next, change the fileWrapperRepresentationOfType: method so that the document can save its contents
according to the type of data requested.
- (NSFileWrapper *)fileWrapperRepresentationOfType:(NSString *)type
{
NSRange range = NSMakeRange(0, [[textView string] length]);
if ([type isEqualToString:@"Rich Text with Attachments"]) {
return [[textView textStorage] RTFDFileWrapperFromRange:range
documentAttributes:nil];
} else {
NSFileWrapper * wrapper = [[NSFileWrapper alloc]
initRegularFileWithContents:[textView RTFFromRange:range]];
return [wrapper autorelease];
}
}
5. Finally, change the windowControllerDidLoadNib: method so that graphics can be added to the documents.
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
[super windowControllerDidLoadNib:aController];
if (rtfData) {
[[textView textStorage]
replaceCharactersInRange:NSMakeRange(0, [[textView string] length]);
withAttributedString:rtfData];
[rtfData release];
}
[textView setAllowsUndo:YES];
[textView setImportsGraphics:YES];
}
6. Save the project (File
7. Clean the project (Build
Save, or
-S).
Clean, or Shift-
[1]
-K).
8. Build and run ( -R) the application. Create a document, and drag an image into it. When you save the document, the
Save panel will have a pull-down menu to select what kind of file you are saving, as shown in Figure 11-12. Be sure to
select Rich Text with Attachments in order to save your image information .
www.it-ebooks.info
IBOutlet NSTextView * textView;
NSData * dataFromFile;
NSString * dataType;
}
- (IBAction)analyzeText:(id)sender;
- (IBAction)removeFormatting:(id)sender;
- (IBAction)speakText:(id)sender;
@end
2. Save the MyDocument.h file; then open MyDocument.nib in Interface Builder.
3. Reparse the MyDocument.h file by dragging the MyDocument.h file from Project Builder to Interface Builder's
MyDocument.nib window.
4. Add a button to our document interface, and name it Speak, as shown in Figure 11-13.
Figure 11-12. Saving a file with attachments
5. Control-drag a connection from the Speak button to the File's Owner object, and connect it to the speakText: method.
6. Save (
7. In Project Builder, edit the MyDocument.m file, and add the speakText: method as shown:
- (IBAction)speakText:(id)sender
{
[textView startSpeaking:sender];
}
www.it-ebooks.info
8. Save the project (File
Save, or
-S).
9. Build and run ( -R) the application. Type some text, then click on the Speak button. The built-in Text-to-Speech engine
will start reading off what you typed.
[1]
In some of the versions of Project Builder that we worked with while writing this book, there was a problem with adding document
types unless you forced this cleaning step.
www.it-ebooks.info
11.7 Exercises
1. Using Interface Builder, turn on image attachments and undo by removing the two
lines of code in the awakeFromNib method that perform this duty.
2. Set the ruler to appear automatically when a document window opens.
3. Replace the Speak buttons with menu items.
4. Add a number of characters line to the Analyze sheet.
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
5. Drag the Ripples Blue image from the MainMenu.nib window to the Image View that we
just added. When you drop the image onto the view, you should see the image appear there.
Print menu item to the Image View. The
6. Control-drag a connection from the File
inspector will pop up, if it's not already open, and indicate that the menu item is connected
to FirstResponder.print:. Disconnect this connection, and connect the File
Print menu item to the print: action of the view, as shown in Figure 12-2. Be careful
when dragging the connection, as it's easy to select the window as the target of the action.
www.it-ebooks.info
7. Save (
-R).
www.it-ebooks.info
10. Click the Preview button. The application prints to a PDF file that will be displayed in the
Preview application. Repeat the process, and click Print if you want to see the results
printed on your printer.
Now that you have printed the view, notice that the entire image view drew itself onto the printerborder and all-and appears just as it did onscreen. If you just wanted to draw the image itself to the
printer, you'd need to add a little code to control what gets printed. In the next section, we'll do just
that with text.
www.it-ebooks.info
www.it-ebooks.info
6. Save (File
, or
7. In Project Builder, open MyDocument.m and add the printShowingPrintPanel: method as shown here:
- (void)printShowingPrintPanel:(BOOL)flag
{
NSPrintInfo * printInfo = [self printInfo];
NSPrintOperation * printOp;
printOp = [NSPrintOperation printOperationWithView:textView
printInfo:printInfo];
[printOp runOperation];
}
// a
// b
// c
// d
Save, or
-S).
Print, or
b. Click either the Print button (to send the print job to your printer) or the Preview button (to send the print
information to the Preview application, as shown in Figure 12-5). We recommend that you use the Preview
button as you work through this chapter so that a few sheets of paper can be saved.
Figure 12-4. Printing from a document-based application
www.it-ebooks.info
www.it-ebooks.info
// a
// b
[printInfo setHorizontallyCentered:NO];
[printInfo setVerticallyCentered:NO];
// c
// d
Measurement in Inches
1/8
18
1/4
27
3/8
36
1/2
45
5/8
54
3/4
63
7/8
www.it-ebooks.info
72
Save, or
-S).
3. Build and run ( -R) the application. Now, when you print out a block of text, it will be printed starting at the
upper-left corner, as shown in Figure 12-6.
Figure 12-6. Printing at the top-left corner
Complete information on the settings for a print info object can be found in the /Developer/Documentation/Cocoa/
TasksAndConcepts/ProgrammingTopics/Printing/index.html file installed on your hard drive with the Developer Tools.
www.it-ebooks.info
12.4 Exercises
1. Add a Font menu to the application, and print out files with various fonts.
2. Add printing to the Dot View application that we created in Chapter 8.
3. Resize the image view in View Print to occupy the entire window (don't forget to
set the Autosizing attributes!), and experiment with printing other images.
www.it-ebooks.info
www.it-ebooks.info
All of the contents of a bundle exist in the aptly named Contents directory. At a very
minimum, a bundle consists of two files-Info.plist and PkgInfo-located in the Contents
directory, as shown in Figure 13-2.
Figure 13-2. A minimal bundle
www.it-ebooks.info
The Info.plist file is an XML-based property list file that specifies the following:
www.it-ebooks.info
www.it-ebooks.info
New Project
Application
3. In the next sheet that drops down, make sure that the Copy items checkbox is clicked, as shown in Figure 13-3, and click the Add button.
Figure 13-3. Adding files to the project
www.it-ebooks.info
Save, or
-S).
www.it-ebooks.info
7. Create a subclass of NSObject in Interface Builder. To do this, click on the Classes tab of the MainMenu.nib window, find and Controlclick on NSObject, and then select Subclass NSObject from the pop-up menu. Name the subclass "Controller".
You can also create a subclass by locating NSObject in the Classes pane and hitting the Return key. A new
subclass will be created, and all you need to do is enter a new name for the subclass.
8. Create an outlet named imageView on the Controller object using the Inspector, as shown in Figure 13-5. Type the outlet as
NSImageView.
Figure 13-5. Adding an imageView outlet to the Controller class
www.it-ebooks.info
-F).
-I).
11. Control-drag a connection from the Controller object to the image view. Hook up the connection to the imageView outlet in the Info
window.
12. Save the nib file (
www.it-ebooks.info
{
NSBundle * mainBundle = [NSBundle mainBundle];
NSString * path = [mainBundle pathForResource:@"Abstract 1"
ofType:@"jpg"];
NSImage * image = [[NSImage alloc]initWithContentsOfFile:path];
[imageView setImage:image];
[image release];
// a
// b
// c
// d
// e
}
@end
The code we added performs the following tasks:
a. Gets a reference to the bundle object from which this application was loaded.
b. Uses the pathForResource:ofType: method of the NSBundle class to look up the path of the Abstract 1.jpg file in the
application bundle. If we were to print out the path that results, it would be as follows:
~/LearningCocoa/Image Bundle/build/Image Bundle.app/Contents/Resources/Abstract 1.jpg
c. Creates an NSImage object using the file in our application bundle.
d. Tells the imageView of our application interface to display the image.
e. Releases the image, now that we are done with it and the image view has it.
14. Build and run (
-R) the application. The application should look like Figure 13-6.
Figure 13-6. Image Bundle running and showing Abstract 1.jpg
www.it-ebooks.info
15. Open the Products group in the Groups & Files pane, and examine the Image Bundle.app item, shown in Figure 13-7. This is the built
application bundle and all of the resources inside of it. During the build process, Project Builder automatically moves the image files that we
added to the project into the Resources directory of the application bundle.
Figure 13-7. Examining the built application bundle for Image Bundle
www.it-ebooks.info
Instead of just obtaining specific files from the application bundle, we can get all of the resources of a particular type. To illustrate this, we'll add a
Next button to the application, which will iterate over the set of images in our application.
1. Edit the Controller.h file, and add the following code:
#import <Cocoa/Cocoa.h>
@interface Controller : NSObject
{
IBOutlet NSImageView *imageView;
NSArray * imagePaths;
int currentImage;
}
www.it-ebooks.info
- (IBAction)nextImage:(id)sender;
@end
This allows us to keep track of the paths to all the images in the bundle, as well as keep a count of what image we're showing. In addition, it
adds the method declaration for the action method.
2. Save (
Read
www.it-ebooks.info
6. Connect the Next button to the nextImage: action method on the Controller instance object.
7. Save the nib, and return to Project Builder.
8. Modify the awakeFromNib method in Controller.m to match the following code. Note that we have changed lines b and c from the
previous implementation of this method.
- (void)awakeFromNib
{
NSBundle * mainBundle = [NSBundle mainBundle];
imagePaths = [mainBundle pathsForResourcesOfType:@"jpg"
inDirectory:nil];
[imagePaths retain];
currentImage = 0;
NSImage * image = [[NSImage alloc]initWithContentsOfFile:
[imagePaths objectAtIndex:currentImage]];
// a
// b
// c
// d
www.it-ebooks.info
[imageView setImage:image];
[image release];
}
This code performs the following tasks:
a. Obtains an array of paths for all the JPEG files in our application. The nil argument tells the method to look in the default Resources
directory. If the images were located in a subdirectory of the bundle, we could specify that subdirectory here as well.
b. Retains the reference to the imagePaths array so that it doesn't disappear out from under us.
c. Sets the currentImage counter to 0.
d. Creates a new NSImage object using the first path of the array of paths we obtained in line a.
9. Add the nextImage: action method to Controller.m as follows:
- (IBAction)nextImage:(id)sender
{
currentImage++;
if (currentImage == [imagePaths count]) {
currentImage = 0;
}
NSImage * image = [[NSImage alloc]initWithContentsOfFile:
[imagePaths objectAtIndex:currentImage]];
[imageView setImage:image];
[image release];
}
The code we added performs the following tasks:
a. Increments the image at which we want to look by 1.
// a
// b
// c
// d
www.it-ebooks.info
b. Checks to see if we've incremented the counter past the number of images we have. If so, we reset the counter to 0.
c. Creates an NSImage object using the path at the current index.
d. Sets the image view to display the new image.
10. Save the project (File
11. Build and run (
Save, or
-S).
-R) the application. You should now be able to step through the sequence of images in the bundle.
www.it-ebooks.info
- (void)awakeFromNib
{
NSBundle * mainBundle = [NSBundle mainBundle];
NSArray * imagePaths = [mainBundle pathsForResourcesOfType:@"jpg"
inDirectory:nil];
images = [[NSMutableArray alloc] init];
int count = [imagePaths count];
int i;
for (i = 0; i < count; i++) {
NSImage * image = [[NSImage alloc] initWithContentsOfFile:
[imagePaths objectAtIndex:i]];
[images addObject:image];
[image release]
}
currentImage = 0;
[imageView setImage:[images objectAtIndex:currentImage]];
}
// a
// b
// c
// d
www.it-ebooks.info
{
currentImage++;
if (currentImage == [images count]) {
currentImage = 0;
}
[imageView setImage:[images objectAtIndex:currentImage]];
// a
// b
}
The code we added does the following things:
a. Checks to see if we have incremented the counter past the number of images loaded. If so, it resets the counter to 0.
b. Sets the image displayed into the image view to the next image.
4. Save the project (File
Save, or
-S).
5. Build and run ( -R) the project. You'll notice that it takes longer for the application to launch than it did before, but switching between
images is now much quicker. As with most performance optimizations, the price of loading the images has to be paid somewhere; it's just a
matter of when the price is paid.
The real answer to our performance problem is a background thread that loads the images after the first image is
loaded and displayed. Doing this would move the price of loading the images to after the application was already
displayed, when the user wouldn't care. However, using threads is not easy and is an advanced topic beyond the scope
of this book.
www.it-ebooks.info
13.3 Exercises
1. Add a Previous button to the Image Bundle application.
2. Add keyboard shortcuts for both the Next and Previous buttons.
www.it-ebooks.info
www.it-ebooks.info
4. Open TextEdit again, and notice how the menu items are now in French rather than English,
as shown in Figure 14-2.
Figure 14-2. TextEdit running in French
www.it-ebooks.info
Quit, or
What just happened here? By changing the System Preferences, TextEdit uses Mac OS X's and
Cocoa's internationalization system to display the correct interface for the locale we specified.
Under the hood, the system is using localized interface components stored within separate files and
directories within the application's bundle.
www.it-ebooks.info
www.it-ebooks.info
As you can see, the same files, Clock2.niband InfoPlist.strings, exist in various subfolders
of the Resources folder. These subfolders, which have a language name or country code
and a .lproj extension, contain the language-support files for the project.
Mac OS X defines localizations using three different conventions. Each convention allows
a different degree of specificity.
A language name
Languages supported by Mac OS X include English, French, German, Japanese,
Chinese, Spanish, Italian, Swedish, and Portuguese.
A language abbreviation
The language abbreviations, some of which are shown in Table 14-1, conform to
the ISO 639 specification.
www.it-ebooks.info
Chinese
zh
Danish
da
Dutch
nl
English
en
French
Fr
Korean
Ko
Polish
Pl
A locale abbreviation
The locale abbreviations consist of a language abbreviation (see Table 14-1),
followed by an underscore and a two letter code. These codes, some of which are
shown in Table 14-2, conform to the ISO 3166 specification that can identify a
regional variant of a language.
British English
Locale abbreviation
en_UK
www.it-ebooks.info
American English
en_US
Canadian French
fr_CA
Tawainese Chinese
zh_TW
zh_CN
A common practice of developers is to use the traditional language name for those that
exist, then to use the language abbreviation, and then-only when necessary-the regional
variant abbreviation.
www.it-ebooks.info
www.it-ebooks.info
4. Create a subclass of NSObject. Click on the Classes tab of the MainMenu.nib window,
find NSObject, Control-click it, and select Subclass NSObject from the pop-up menu;
name the subclass Controller.
5. Create an outlet named statusField on the Controller object, using the Info window
Show Info, or Shift- -I).
(Tools
6. Create an action for the Controller subclass, named submit:.
7. Create the files for the Controller subclass (Classes
www.it-ebooks.info
Instantiate Controller).
9. Control-drag a connection between the Controller object and the Not Submitted text field,
and connect it to the statusField outlet in the Info window.
10. Connect the Submit button to the submit: method of the Controller by Controldragging a connection from the Submit button to the Controller object.
11. Save the nib file ( -S), close the MainMenu.nib window in Interface Builder, and then
return to Project Builder.
12. In Project Builder's Groups & Files panel, click the disclosure triangle beside the
MainMenu.nib file, and select the English entry underneath it, as shown in Figure 14-5.
Figure 14-5. Looking at the single localization of the MainMenu.nib file
Show Info, or
-I).
14. Select Add Localized Variant from the Localization & Platforms pop-up menu. You will be
prompted for a locale. Enter French, as shown in Figure 14-6.
www.it-ebooks.info
15. Close the Info window by clicking on the red close window button.
16. You should see two localizations of the MainMenu.nib file in Project Builder.
17. Double-click the French variant to open it in Interface Builder.
18. Modify the various strings in the interface to match Figure 14-7. To type in the character,
type Option-e, then e again (without the Option key). Notice that you'll have to resize the
window and move things around to accommodate the longer text fields.
Figure 14-7. Our interface in French
www.it-ebooks.info
-Q).
22. Launch the System Preferences application in the Dock, and click on the International
button to open its preferences panel.
23. Change your language preferences from English to Franais by clicking on Franais and
dragging it to the top of the list in the Languages window.
24. Run the application again to see the French interface run.
25. Quit and return to the System Preferences; reset your preferred language to English by
dragging it to the top of the list.
www.it-ebooks.info
www.it-ebooks.info
Show Info, or
-I).
4. Select Add Localized Variant from the Localization & Platforms pop-up menu. You will be prompted for
a localization. Enter French, and then close the Info window.
5. Click on the disclosure triangle next to the Localizable.strings file. Beneath that, you will see two files:
English, which we created in steps 1 and 2; and French, which we created in the previous step.
6. Edit the French variant of the file to match the following text:
"Not Submitted" = "Non soumis";
"Accepted" = "Admis";
7. Edit the submit: method of the Controller.m file.
- (IBAction)submit:(id)sender
{
[statusField setStringValue:NSLocalizedString(@"Accepted", nil)];
}
This code sets the value of our status field to the localized form of the Accepted string.
8. Save the project (File
9. Build and run (
changes.
Save, or
-S).
-R) the application. Test out the Submit button, and make sure that the status field
10. Quit the application, and set Franais as your preferred language using System Preferences.
11. Run the application again to see the French interface run. Click the Submit button.
12. Quit and return your preferred language to English.
www.it-ebooks.info
14.5 Exercises
1. Localize the Currency Converter project from Chapter 5 into the language of your
choice. Use the Translation channel of Sherlock 3 if you need help translating the
strings.
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
Application
Cocoa
4. Create a subclass of NSObject in Interface Builder. Click on the Classes tab of the MainMenu.nib window, find
NSObject, Control-click it, and select Subclass NSObject from the pop-up menu. Name the subclass "Controller".
5. Create the following outlets on the Controller class:
bookField
colorField
foodField
cityField
6. Create an action named textFieldChanged:. This action will tell the preferences database when an item has changed.
7. Generate the source-code files for the Controller class (Classes
8. Instantiate the Controller class (Classes
Instantiate Controller).
9. Connect the four text fields on the user interface to their respective outlets by control-dragging a connection from the
Controller instance to each of the fields in turn.
10. Connect each of the four text fields to the Controller's textFieldChanged action method by control-dragging a
connection from the text field to the Controller instance.
11. Save (File
Save, or
12. Edit the Controller.h file as shown, adding an instance variable that will hold a reference to a NSUserDefaults object.
#import <Cocoa/Cocoa.h>
@interface Controller : NSObject
www.it-ebooks.info
{
IBOutlet id bookField;
IBOutlet id cityField;
IBOutlet id colorField;
IBOutlet id foodField;
NSUserDefaults * prefs;
}
- (IBAction)textFieldChanged:(id)sender;
@end
13. Add an init method to the Controller.m file. This will set the prefs instance variable.
- (id)init
{
[super init];
NSMutableDictionary * defaultPrefs = [NSMutableDictionary dictionary];
[defaultPrefs
[defaultPrefs
[defaultPrefs
[defaultPrefs
// a
// b
// c
// d
return self;
}
This code does the following things:
a. Creates a new mutable dictionary that will serve as the container for the default values the application will use
b. Sets four key/value pairs that correspond to the default values we want to store in the preferences system
c. Obtains a reference to the preferences system
d. Indicates to the prefs object that we want to use the defaultPrefs dictionary as the set of default preferences
14. Add a dealloc method so that the class cleans up after itself properly.
- (void)dealloc
{
[prefs release];
[super dealloc];
}
15. Add an awakeFromNib method to populate the user interface from any settings that are in the prefs object.
- (void)awakeFromNib
{
[bookField setStringValue:[prefs stringForKey:@"FavBook"]];
[cityField setStringValue:[prefs stringForKey:@"FavCity"]];
[colorField setStringValue:[prefs stringForKey:@"FavColor"]];
[foodField setStringValue:[prefs stringForKey:@"FavFood"]];
}
16. Implement the textFieldChanged: action method so that the key values are saved as they change to the prefs object.
- (IBAction)textFieldChanged:(id)sender
{
if (sender == bookField) {
www.it-ebooks.info
[prefs setObject:[bookField stringValue] forKey:@"FavBook"];
} else if (sender == cityField) {
[prefs setObject:[cityField stringValue] forKey:@"FavCity"];
} else if (sender == colorField) {
[prefs setObject:[colorField stringValue] forKey:@"FavColor"];
} else if (sender == foodField ){
[prefs setObject:[foodField stringValue] forKey:@"FavFood"];
}
}
17. Build and run (
-R) the application. You should see the interface launch as shown in Figure 15-2.
Figure 15-2. Our application running
18. Change some of the values. Quit the application, then restart to see if the values were saved.
19. Find the Favorites.plist file in your ~/Library/Preferences folder. Double-click the file to launch the Property List Editor (/
Applications/Utilities), which allows you to see the values that you changed in the file, as shown in Figure 15-3. To see the
XML representation of the plist file, click the Dump button. You can also, if needed, edit the property list here, save the
changes, and then launch the application we built to see the changes.
Figure 15-3. The preferences file under the hood
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
-R) the application, change the values, and quit the application.
3. Using the Finder, you can see that there is now a com.oreilly.Favorites.plist file in
your ~/Library/Preferences folder. If you wanted to read this file from the
command line, you could use the following command:
defaults read com.oreilly.Favorites
www.it-ebooks.info
15.5 Exercises
1. Take a look at the contents of the Favorites preference list file using Property List
Editor.
2. Add a reset button to the Favorites application that will reset the values to their
original state.
3. Modify the Favorites application so that it reads its application defaults from a .
plist file contained as a resource in its application bundle.
4. Modify the Currency Converter application from Chapter 5 to remember the
exchange rate between invocations of the application.
www.it-ebooks.info
www.it-ebooks.info
As you can see from Figure 16-1, you can assign the File's Owner object to any class.
When creating an auxiliary nib file, you will need to assign the File's Owner proxy to the
class that will load the nib and be responsible for mediating between the functionality of
www.it-ebooks.info
www.it-ebooks.info
Application
Cocoa
Table 16-1. Assigning the types for the outlets of the Simple Inspector application
Outlet
Type
infoPanel
NSPanel
infoPanelController
NSWindowController
textLengthField
NSTextField
textView
NSTextView
The infoPanel outlet will hold a reference to our inspector panel. The infoPanelController will serve as the
www.it-ebooks.info
window controller for the panel. The textFieldLength outlet will point to a text field in our inspector panel that
will display the total number of characters in the textView.
6. Generate the source-code files for the Controller class (Classes
7. Instantiate the Controller class (Classes
Instantiate Controller).
8. Drag a text view out to the application's main window, and set its Autosizing attributes so that it will resize along with
the window that contains it.
9. Control-drag a connection from the Controller instance object to the text view, as shown in Figure 16-3, and connect it
to the textView outlet.
Figure 16-3. Connecting the text view to the Controller
10. Add a Show Info menu item to the MainMenu's Window menu, as shown in Figure 16-4. Give it a key equivalent of
-I. This will let the user of our application get the info panel either by using the menu item or by just using the
Command-key equivalent.
From the Cocoa-Menus palette:
a. Drag an Item to the menu, and place it as shown in Figure 16-4.
b. Use the Info panel to change its name and assign a Command-key equivalent.
Figure 16-4. Adding a Show Info window to the application's menu bar
www.it-ebooks.info
11. Connect the Show Info menu item to the Controller's showInfoPanel: action method by Control-dragging a
connection from the menu item to the Controller object, as shown in Figure 16-5.
Figure 16-5. Connecting the menu item to the showInfoPanel: action method
12. Save (
www.it-ebooks.info
2. Save the file as InfoPanel.nib in your ~/LearningCocoa/Simple Inspector /English.lproj folder, as shown in Figure 167. You will be prompted to add the nib file to the Simple Inspector target; click the Add button to do so.
Figure 16-7. Saving the InfoPanel.nib file
3. Now, we need to designate our Controller class as the object that will be the File's Owner for this nib file at
runtime. To let Interface Builder know that the Controller class exists, drag the Controller.h file from Project
Builder onto the InfoPanel.nib window.
4. Select the File's Owner proxy object in the InfoPanel.nib window, and, using the File's Owner Info inspector as shown
in Figure 16-8, set the File's Owner class to Controller.
Figure 16-8. Setting the File's Owner class
www.it-ebooks.info
This tells Interface Builder that the class responsible for loading this nib file will be of type Controller. By setting this
class as the File's Owner, we can designate that various interface components in this nib file should be connected to the
outlets of the Controller class.
5. Drag out a panel from the Windows palette. Name the panel Info by using the NSPanel Info inspector, as shown in
Figure 16-9. Also, make sure that the Hide on deactivate and Utility window (Panel only) options are checked. These
options will make our panel look like an Info panel and ensure that it disappears when our application is not active.
Figure 16-9. Creating the Info panel
6. Drag two text fields onto the panel; name them "Length of Text View:" and "Number". Control-drag a connection
from the File's Owner proxy object (remember, this is a stand-in for our Controller class) to the Number field, and set
the textLengthField outlet, as shown in Figure 16-10.
www.it-ebooks.info
7. Control-drag a connection from the File's Owner object to the Panel object in the InfoPanel.nib window, as shown in
Figure 16-11, and connect it to the infoPanel outlet.
Figure 16-11. Connecting the infoPanel outlet
// a
// b
// c
// d
// e
www.it-ebooks.info
}
@end
The code we added performs the following tasks:
a. Checks to see if we have a reference to a window controller for the Info panel. If we don't, it means that we
need to load the nib file that contains the panel. On the other hand, if we have a reference, then we don't need
to load it.
b. Loads the InfoPanel nib file. Notice that we don't use the .nib extension here. When the nib is loaded, the
connections assigned to the File's Owner will be made to the Controller object.
c. Creates a new window controller, assigns it to the infoPanelController variable, and initializes it to use
the panel loaded from the nib.
d. Sets the textLengthField to the length of the textStorage object.
e. Shows the Info panel.
2. Build and run ( -R) the application. Enter some text into the text view, then show the Info panel (Window
Show Info or -I). You should see something like Figure 16-12.
Figure 16-12. Our inspector in action
Obviously, we could add all sorts of information to our inspector window, such as the number of words in the file, number of
paragraphs, etc. We'll leave these tasks as exercises at the end of the chapter.
// a
www.it-ebooks.info
{
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(textDidChange:)
name:NSTextDidChangeNotification
object:textView];
}
- (void)textDidChange:(NSNotification *)notification
{
[textLengthField setIntValue:[[textView textStorage] length]];
}
// b
- (IBAction)showInfoPanel:(id)sender
{
if (!infoPanelController) {
[NSBundle loadNibNamed:@"InfoPanel" owner:self];
infoPanelController = [[NSWindowController alloc]
initWithWindow:infoPanel];
}
[textLengthField setIntValue:[[textView textStorage] length]];
[infoPanelController showWindow:self];
}
@end
The code we added performs the following tasks:
a. Adds an awakeFromNib method that will add the Controller instance as an observer to the default
notification center interested in NSTextDidChangeNotification events on the textView object.
b. Implements the callback method that the notification center will call whenever text changes in the text view.
We simply update the textLengthField in our inspector panel.
2. Build and run ( -R) the application. Show the Info panel (Window
Show Info or -I), then type text into the
text view. The info panel will now keep up with the correct number of characters in the text view as you type.
www.it-ebooks.info
16.3 Exercises
1. Add a field to the Simple Inspector application that will display the number of
words in a document.
2. Add a field to the Simple Inspector application that will display the number of
paragraphs in a document.
3. Add an Info window to Dot View (see Chapter 8) that will let the user know the
current diameter of the dot.
www.it-ebooks.info
www.it-ebooks.info
Thankfully, this is easy to fix. Instead of creating a new application, as we've done to
introduce most of the topics in the book, we'll add all of these finishing touches to the Dot
View application we built in Chapter 8.
1. Open the Dot View project from your ~/LearningCocoa/DotView folder.
2. Open the MainMenu.nib file in Interface Builder.
3. Click on the NewApplication menu, as shown in Figure 17-2, and change the menu
text "NewApplication" in the various menu items to read "Dot View". You can do
this either by modifying the title of the menu item using the inspector, as shown in
Figure 17-2, or by editing the menu items from the MainMenu.nib window.
www.it-ebooks.info
If you choose to edit the menu items from the MainMenu.nib window, click once
on the applicable menu, and then double-click on an item (for example, "Quit
NewApplication") to highlight it. Next, double-click again on "NewApplication" to
highlight just that word, and type in the name of the application-in this case, Dot
View.
4. Click on the Help menu item, and change the "NewApplication" help menu item to
read "Dot View Help".
5. Save the nib file (
6. Build and run (
www.it-ebooks.info
www.it-ebooks.info
2. Create your icon. The icon that we designed (and which you can get from the
example download package) is shown in Figure 17-4.
Figure 17-4. The Dot View icon in Photoshop
www.it-ebooks.info
www.it-ebooks.info
10. Build and run ( -R) the application. When the application launches, you probably
won't see the new icon in the Dock; but, when you try to close the window, you
will see it in the sheet, as shown in Figure 17-7.
Figure 17-7. The icon file in use by the application
www.it-ebooks.info
When you run the Dot View application from Project Builder or
out of the build directory, you won't always see the icon used in
the Dock or in the Finder. However, as soon as you drag the built
application to another directory (e.g., ~/Applications), the icon
will probably work correctly. Sometimes logging out and back
into your machine is required. This behavior is due to the Finder
caching the icons for display on screen.
Chapter 10 of Apple's Inside Mac OS X: Human Interface Guidelines contains quite a bit of
information about icon design. You can find this book on your system in the /Developer/
Documentation/Essentials/AquaHIGuidelines folder.
www.it-ebooks.info
www.it-ebooks.info
New File
www.it-ebooks.info
5. The last step is to add two keys to the application's property list. Unlike setting the icon file,
Project Builder doesn't yet provide a GUI for setting these properties, so use the following
directions:
a. Open the main target of the application (Project
b. Navigate the outline view to Info.plist Entries
17-10.
6. Click the New Sibling button. Name the new key CFBundleHelpBookFolder, and give
it a string value of Help.
7. Click the New Sibling button again. Name the new key CFBundleHelpBookName, and
give it a string value of Dot View Help.
Build and run ( -R) the application. When the application launches, ask for Help (Help
Dot View Help, or use the -? keyboard shortcut). The Apple Help application will launch and
display your help, as shown in Figure 17-11.
www.it-ebooks.info
www.it-ebooks.info
This default about box displays the application icon-in our case, the icon we added earlier
in the chapter-the application name, and a couple of strings obtained from the application
bundle. To set these strings to something a bit more sensible, use the following steps.
1. Open the InfoPlist.strings file in the Resources folder of the Groups & Files panel,
as shown in Figure 17-13.
Figure 17-13. Editing the InfoPlist.strings file
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
Once you create an RTF file and add it to Project Builder, you
can edit it directly in Project Builder. However, we couldn't start
by creating the RTF file directly in Project Builder. Hopefully,
the Project Builder team will add this functionality in future
revisions to the Developer Tools.
www.it-ebooks.info
www.it-ebooks.info
from large amounts of memory swapping to and from disk, thus increasing your user's
experience.
www.it-ebooks.info
www.it-ebooks.info
6. Click the Save button. After a few status boxes go by, you should have a Dot View.
dmg file ready for distribution. Double click on the .dmg file, and mount it as a disk
drive.
www.it-ebooks.info
17.7 Closure
After 17 chapters, you should now have a working foundation of how to build simple
Cocoa applications. (And it is quite likely that you have finished the book in a much
shorter time than it took to write!) To be sure, we haven't covered every topic in depth, but
we have given you enough knowledge to be dangerous. These are the next steps you should
take in your quest to become a great Cocoa programmer:
Read more about Cocoa. We noted a few books on Cocoa in the Preface, which you
should check out.
Write applications. There's no substitute for experience.
There's still a lot you will need to learn to master Cocoa programming, but you should now
be over the initial "huh?" stage and ready to dive in deep.
Good luck!
www.it-ebooks.info
17.8 Exercises
1. Provide an icon, "help", and customize the about box for the Simple RTF Edit
application from Chapter 11.
2. Package up for distribution the Simple RTF Edit application.
3. Write a complete application from scratch, utilizing the knowledge that you've
gained from this book.
www.it-ebooks.info
Part V: Appendixes
The appendixes include quick-reference material for learning more about
Cocoa's Objective-C classes. In addition, they list resources beyond the
scope of this book, to expand your Cocoa development horizon.
This section includes the following appendixes:
Appendix A
Appendix B
Appendix C
www.it-ebooks.info
www.it-ebooks.info
A.1 Chapter 2
1. Open a Finder window, and navigate to the /Developer/Applications folder. Drag
the Project Builder and Interface Builder icons to your Dock.
2. Open a Finder window and navigate to the /Developer/Documentation/Cocoa
folder. Drag the CocoaTopics.html file to your Dock.
www.it-ebooks.info
A.2 Chapter 3
1. One way to access the documentation:
Open the /Developer/Documentation/Cocoa/CocoaTopics.htmlfile in your web
browser of choice, click on the Foundation link in the Objective-C Framework
Reference section, and then follow the NSObject and NSString links.
2. One way to find the documentation:
In the Foundation reference document that the NSObject and NSString links
are on, go to the bottom of the page, and click on the Functions link. You'll find the
NSLog documentation on this page.
Use the Find functionality of your browser (Edit
Find, or -F on most browsers) to search for the NSLog
documentation on this rather large page.
www.it-ebooks.info
A.3 Chapter 4
1. Here's one way to do it:
Open the strings project we created, and place a breakpoint before the [artist release] statement. Debug the
application, and enter in the following commands into the gdb console:
(gdb) print-object [artist lowercaseString]
(gdb) print-object [artist uppercaseString]
2. One possible solution is to create a project named "fileprinter" with the following main.m file:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if (argc > 1) {
NSString * filename = [NSString stringWithCString:argv[1]];
NSString * file = [NSString stringWithContentsOfFile:filename];
printf("%s\n", [file UTF8String]);
}
[pool release];
return 0;
}
When executed from Project Builder, nothing will happen, but if you go to the terminal and issue the following
commands, you will see the contents of the main.m file printed:
[titanium:~] duncan% cd ~/LearningCocoa/fileprinter
[titanium:~/LearningCocoa/fileprinter] duncan% build/fileprinter main.m
Try this with a few other files.
3. One way to look up this documentation:
Open the /Developer/Documentation/Cocoa/CocoaTopics.html file in your web browser of choice, click on the
Foundation link in the Objective-C Framework Reference section, and then follow the NSArray, NSSet and
NSDictionary links.
4. One solution is to add the following line of code to the main method:
[array writeToFile:@"foo.plist" atomically:YES];
When you run the arrays program with this additional line, a foo.plist file will be written into ~/LearningCocoa/arrays/
build containing the elements of the array.
5. One possible solution is to create a project named "dictionarysaver" with the following main.m file:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary * dict = [NSMutableDictionary dictionary];
[dict setObject:@"The Meaning of Life" forKey:@"A String"];
[dict setObject:[NSNumber numberWithInt:42] forKey:@"An Integer"];
www.it-ebooks.info
[dict writeToFile:@"dict.plist" atomically:YES];
[pool release];
return 0;
}
When you run this program, the dictionary will be written to the dict.plist file in the ~/LearningCocoa/dictionarysaver
folder. This file will look as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.
com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>A String</key>
<string>The Meaning of Life</string>
<key>An Integer</key>
<integer>42</integer>
</dict>
</plist>
6. The dictionaries example application doesn't release the dict object. The following line of code is needed after the
printf statement:
[dict release];
www.it-ebooks.info
A.4 Chapter 5
1. One way to do this is to select the text labels, then change the font using the Font
Show Fonts, or -T).
Panel (Format
2. One way to do this is to select the totalField, then change the font color using
the Font Panel. To access the color picker, use the Extras... pull-down menu on the
Font Panel.
www.it-ebooks.info
A.5 Chapter 6
1. The documentation can be found in the /Developer/Documentation/Cocoa /
CocoaTopics.html folder. Click on the Application Kit link in the Objective-C
Framework Reference section, and then follow the NSWindow and NSView links.
2. The easiest way to do this is to select the window in Interface Builder and change
the title using the Attributes inspector. You can bring the inspector up by hitting
Shift- -I.
3. One way to do this is to edit the Controller.h file directly to match the following
code:
@interface Controller : NSObject
{
IBOutlet id converter;
IBOutlet NSTextField dollarField;
IBOutlet NSTextField rateField;
IBOutlet NSTextField totalField;
}
- (IBAction)convert:(id)sender;
@end
www.it-ebooks.info
A.6 Chapter 7
1. One way to do this is to edit the drawRect: method as follows:
- (void)drawRect:(NSRect)rect
{
[[NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:1.0] set];
NSRectFill([self bounds]);
}
2. The easiest way to do this is to add the drawing code from the String View application into the Line View
drawRect: method as follows:
- (void)drawRect:(NSRect)rect
{
NSRect bounds = [self bounds];
NSPoint bottom = NSMakePoint((bounds.size.width/2.0), 0);
NSPoint top = NSMakePoint((bounds.size.width/2.0), bounds.size.height);
NSPoint left = NSMakePoint(0, (bounds.size.height/2.0));
NSPoint right = NSMakePoint(bounds.size.width, (bounds.size.height/2.0));
[[NSColor whiteColor] set];
[NSBezierPath fillRect:bounds];
[[NSColor blackColor] set];
[NSBezierPath strokeRect:bounds];
[NSBezierPath strokeLineFromPoint:top toPoint:bottom];
[NSBezierPath strokeLineFromPoint:right toPoint:left];
[[NSBezierPath bezierPathWithOvalInRect:bounds] stroke];
NSString * hello = @"Hello World!";
NSMutableDictionary * attribs = [NSMutableDictionary dictionary];
[attribs setObject:[NSFont fontWithName:@"Times" size:24]
forKey:NSFontAttributeName];
[attribs setObject:[NSColor redColor]
forKey:NSForegroundColorAttributeName];
[hello drawAtPoint:NSMakePoint((bounds.size.width/2.0),
(bounds.size.height/2.0))
withAttributes:attribs];
}
@end
3. One way to do this is to insert the following line of code before drawing any of the lines in Line View:
[NSBezierPath setDefaultLineWidth:3.0];
This will cause all paths to be drawn with 3-point-wide strokes. Try some other values.
4. One way of finding the NSBezierPath documentation is to click the Find tab in Project Builder (or use the menu
Find
Show Batch Find). Enter NSBezierPath into the Find box, and hit Return. After a few moments,
Project Builder will show you all the occurrences of the NSBezierPath string, both in your project and in the
frameworks against which it's linked. To limit the search to just your project, adjust the "This Project" pull-down to
"This Project, no frameworks."
www.it-ebooks.info
www.it-ebooks.info
A.7 Chapter 8
1. The easiest way to accomplish this is to rename the mouseDown: method to mouseDragged:.
2. One way to do this is to modify the initWithFrame: method as follows:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
center.x = frame.size.width / 2.0;
center.y = frame.size.height / 2.0;
radius = 20.0;
color = [[NSColor redColor] retain];
return self;
}
3. One way of changing the setRadius: method is as follows:
- (IBAction)setRadius:(id)sender
{
float newRadius = [sender floatValue];
if (newRadius != radius) {
radius = newRadius;
[self setNeedsDisplay:YES];
}
}
This will ensure that a redraw of the interface only occurs when it is needed.
4. One way to do this is to make the MyDelegate object instance a delegate of NSApplication and
implement the applicationShouldTerminate: method. To do this, Control-drag a connection from
File's Owner to MyDelegate in Interface Builder, then add the following method to the MyDelegate class:
- (NSTerminationReply)applicationShouldTerminate:(NSApplication *)sender
{
int answer = NSRunAlertPanel(@"Quit", @"Are you sure?",
@"Quit", @"Cancel", nil);
if (answer == NSAlertDefaultReturn) {
return NSTerminateNow;
} else {
return NSTerminateCancel;
}
}
5. We suggest changing it to blue by modifying the initWithFrame: method as follows:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
center.x = frame.size.width / 2;
center.y = frame.size.height / 2;
www.it-ebooks.info
radius = 20.0;
color = [[NSColor blueColor] retain];
return self;
}
Of course you can change it to whatever color you please.
6. To do this requires setting the springs appropriately in Interface Builder.
a. Set the springs for the Dot View as shown in Figure A-1.
Figure A-1. Spring settings for Dot View
8. Set the springs for the color well as shown in Figure A-3.
Figure A-3. Spring settings for Dot View's color well
www.it-ebooks.info
www.it-ebooks.info
A.8 Chapter 9
1. Go into Interface Builder, and either edit the column header, either directly or using the inspector.
2. One way to do it is to make the MyDataSource object instance a delegate of NSApplication and
implement the applicationShouldTerminate: method. To do this, Control-drag a connection from
File's Owner to MyDataSource in Interface Builder, then add the following method to the MyDataSource
class:
- (NSTerminationReply)applicationShouldTerminate:(NSApplication *)sender
{
int answer = NSRunAlertPanel(@"Quit", @"Are you sure?",
@"Quit", @"Cancel", nil);
if (answer == NSAlertDefaultReturn) {
return NSTerminateNow;
} else {
return NSTerminateCancel;
}
}
3. The FoodItem class needs a dealloc method to release the name and price variables.
www.it-ebooks.info
A.9 Chapter 10
1. Once again, the documentation can be found in the /Developer/Documentation /Cocoa/CocoaTopics.htmlfile.
2. In the active target panel of Project Builder (Project
settings:
Extensions: plist
-R) the application. You should be able to read and write plist files now.
3. The revert functionality doesn't work because the windowControllerDidLoadNib method isn't called, and that
is where we turn the contents of a file into a string to use. To fix this, edit the loadDataRepresentation:
ofType: method as follows:
- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)type
{
if (textView){
NSString * text = [[NSString alloc]initWithData:dataFromFile
encoding:NSUTF8StringEncoding];
[textView setString:text];
[text release];
} else {
dataFromFile = [data retain];
}
return YES;
}
www.it-ebooks.info
A.10 Chapter 11
1. To turn on the ability for the TextView to handle graphics from within Interface Builder, simply click on the
TextView, and bring up the Attributes inspector, as shown in Figure A-4. Simply click on the radio buttons, and then
remove the corresponding lines of code from MyDocument.m.
Figure A-4. TextView attribute inspector
www.it-ebooks.info
lastAttribute = attributeValue;
count++;
}
NSBeginAlertSheet(@"Analysis",
// title
i
@"OK",
// default button label
nil,
// cancel button label
nil,
// other button label
[textView window],
// document window
nil,
// modal delegate
NULL,
// selector to method
NULL,
// dismiss selector
nil,
// context info
@"Font Changes %i\nCharacter Count %i",
fontChanges, [storage length]);
}
//
www.it-ebooks.info
A.11 Chapter 12
1. The easiest way to do this is to add the Font menu to the MainMenu.nib file. Then,
in your application, use the Font menu to bring up the Font Panel (or hit -T).
2. This is almost a trick question.
Print command works and will print out the Dot View
By default, the File
Print menu item from
window. To print just the Dot View, disconnect the File
FirstResponder.print, then connect it (by Control-dragging) to the
DotView area and the print: action.
3. Simply drag the image view to encompass the entire window. You may even want
to turn the border off so that only the image contained by the view will print. Try
setting the image view to some of the images in the /Library/Desktop Pictures
folder for printing.
www.it-ebooks.info
A.12 Chapter 13
1. One way to do this:
Add the following method to Controller.m, and connect it to a "Previous Image"
button:
- (IBAction)prevousImage:(id)sender
{
if (currentImage == 0) {
currentImage = [images count] - 1;
} else {
currentImage--;
}
[imageView setImage objectAtIndex:currentImage];
}
2. One way to do this is in Interface Builder. In the inspector, set the key equivalent of
the next button to "n" and of the previous button to "p".
Be sure to hit Return after entering in the key equivalent so that it
is stored in the nib file. We had a few problems with this feature
on some builds of Interface Builder until we discovered this.
www.it-ebooks.info
A.13 Chapter 14
1. The best way to do this is to follow the procedure that we detailed in the chapter:
a. Create a localized nib file variant.
b. Modify the various strings in the UI for their new language.
If you don't know another language, or if you just want to check your
translations, use the Translation channel of Sherlock 3.
www.it-ebooks.info
A.14 Chapter 15
1. Find the ~/Library/Preferences/com.oreilly.Favorites file, and double-click it to open it in the Property List Editor (/Developer/
Applications).
2. Create a button on the interface for the Favorites application, and have it call the following action method:
- (IBAction)reset:(id)sender
{
[prefs removeObjectForKey:@"FavBook"];
[prefs removeObjectForKey:@"FavCity"];
[prefs removeObjectForKey:@"FavColor"];
[prefs removeObjectForKey:@"FavFood"];
}
3. One way to do this:
a. Create a default.plist file using Property List Editor. The root key should be of type Dictionary, and it should have four
children. The file should look as follows when you are done with it:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
<key>FavBook</key>
<string>Learning Cocoa</string>
<key>FavCity</key>
<string>San Francisco </string>
<key>FavColor</key>
<string>Red</string>
<key>FavFood</key>
<string>Mexican</string>
</dict>
</plist>
b. Add the default.plist file as a resource to your project.
c. Change the init method as follows:
- (id)init
{
[super init];
NSString file = [[NSBundle mainBundle]
pathForResource:@"default" ofType:@"plist];
NSDictionary * defaultPrefs = [NSDictionary
dictionaryWithContentsOfFile:file];
prefs = [[NSUserDefaults standardUserDefaults] retain];
[prefs registerDefaults:defaultPrefs];
return self;
}
4. One way to do this:
Create a new action method in the Controller.h file as follows:
- (IBAction)rateUpdated:(id)sender
{
[[NSUserDefaults standardUserDefaults] setFloatValue:[sender floatValue]
forKey:@"rate"];
}
www.it-ebooks.info
Hook the rateField text field to the rateUpdated: action method so that it is called whenever the contents of the field are
changed. Next, add an awakeFromNib method to the Controller.h file as follows:
- (void)awakeFromNib
{
[rateField setFloatValue:[[NSUserDefaults standardUserDefaults]
floatForKey:@"rate"];
}
www.it-ebooks.info
A.15 Chapter 16
1. One way to do this:
a. Add an outlet named wordCountField to the Controller class.
b. Add two text labels to the utility panel: the first named "Number of Words" and the second being a
place holder. Connect the place holder to the wordCountField outlet in the File's Owner object
proxy.
c. Add the following code to the showInfoPanel: method:
#import "Controller.h"
@implementation Controller
- (IBAction)showInfoPanel:(id)sender
{
if (!infoPanelController) {
[NSBundle loadNibNamed:@"InfoPanel" owner:self];
infoPanelController = [[NSWindowController alloc]
initWithWindow:infoPanel];
}
[textLengthField setIntValue:[[textView textStorage] length]];
[wordCountField setIntValue:[[textStorage
componentsSeparatedByString:@" "] count];
[infoPanelController showWindow:self];
}
@end
2. Follow the same process as listed earlier with a new paraCountField, and separate the string based on the
\n character.
3. Use the following process to guide you:
a. Add a radiusTextField outlet and a showInfoPanel: action method to the DotView class.
b. Create a new nib file named InfoPanel.nib.
c. Lay out a user interface that allows the radius to be displayed via a radiusTextField outlet.
d. Set the File's Owner of InfoPanel.nib to the DotView class.
e. Add a menu item to the menu bar to show the Info panel, and connect it to the showInfoPanel:
action method.
www.it-ebooks.info
A.16 Chapter 17
We're going to leave the final exercises up to you to complete on your own. By now you
should be able to tackle them without any help from us. However, here are a couple of
ideas for an application to write:
A home inventory keeper that can keep a list of all the items in your house,
complete with serial numbers and date of purchase
An application that is like Dot View, but can draw shapes with a variable number
of sides
Good luck!
www.it-ebooks.info
Your first source of additional information pertaining to the material presented in this book
is the book's own web site, located at the following URL:
http://www.oreilly.com/catalog/learncocoa2/
At this site, you'll find the book's sample code available for downloading, as well as any
errata and plans for future editions.
www.it-ebooks.info
www.it-ebooks.info
interface, known as Aqua. This guide provides examples of how to use such Aqua
interface elements as windows, controls, dialogs, and icons so that the users of your
Cocoa application will be familiar and comfortable with your product the moment
they double-click its icon.
/Developer/Documentation/Essentials/AquaHIGuidelines/AquaHIGuidelines.pdf
Inside Mac OS X: Performance
This book tells you how to enhance your program to achieve maximum
performance and how to use development tools to analyze and tune your code.
Topics include: managing virtual memory; accessing files efficiently; optimizing
Carbon applications; building efficient C, C++, and Java code; using the Mac OS X
performance measurement and analysis tools; and optimizing the in-memory layout
of your program.
/Developer/Documentation/Essentials/Performance/performance.pdf
Core Foundation Developer Documentation
Cocoa is built upon the Core Foundation framework. Occasionally, you will need to
use functionality that is at the Core Foundation level and isn't exposed via the
Cocoa APIs.
/Developer/Documentation/CoreFoundation/corefoundation_carbon.html
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
programming.
http://www.stepwise.com/
The Vermont Recipes
Published on Stepwise, this group of articles written by Bill Cheeseman serves as a
cookbook for developing Mac OS X applications with Cocoa using a no-nonsense,
hands-on, step-by-step approach.
http://www.stepwise.com/Articles/VermontRecipes/index.html
Cocoa Dev Central
This site is updated fairly frequently with tips, tricks, and tutorials for the novice
Cocoa developer.
http://www.cocoadevcentral.com/
CocoaDev Wiki
This user-editable web site is by and for the Mac OS X developer community. If
you've never used a WikiWeb before, this style of site gives literally anyone
capable of viewing the page the ability to add information.
http://www.cocoadev.com/
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
Simply double-click the CocoaTopics.html file, and your browser will open it, showing you
the complete set of Cocoa documentation. While using this book, one of the most important
parts of this documentation set will be the Objective-C Framework Reference, highlighted
in Figure C-2.
Figure C-2. Cocoa Developer Documentation index page
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
Cocoa Browser is also invaluable for seeing which methods a particular class supports and
which arguments can be used. For example, we find ourselves looking at all the methods
that NSResponder and its subclasses support on a frequent basis. In addition, the Cocoa
functions and data types from both the AppKit and the Foundation Kit are easily
browsable, as shown in Figure C-4.
Figure C-4. Browsing Foundation's functions
www.it-ebooks.info
Colophon
Our look is the result of reader comments, our own experimentation, and feedback from
distribution channels. Distinctive covers complement our distinctive approach to technical
topics, breathing personality and life into potentially dry subjects.
The animal on the cover of Learning Cocoa with Objective-C, Second Edition, is an Irish
setter. Bred as a sporting dog in the 19th century, the Irish setter's agility and energy made
it a prime companion for pheasant and quail hunters. By the 1890s, the dog's attractive,
silky red coat and elegant build boosted its popularity as a show dog. For the past century,
breeders have created a larger dog with a longer coat, with deep chestnut red or patches of
red and white hair. The dog is also popular as a family dog. Described as loyal, gentle,
energetic, and happy, the Irish setter gets along well with children. Some hospitals, nursing
homes, and rehabilitation centers also adopt the Irish setter as a therapy dog.
Brian Sawyer was the production editor and proofreader for Learning Cocoa with
Objective-C, Second Edition. Jeff Holcomb was the copyeditor. Claire Cloutier and Sheryl
Avruch provided quality control. Brenda Miller wrote the index.
Emma Colby designed the cover of this book, based on a series design by Edie Freedman.
The cover image is a 19th-century engraving from the Dover Pictorial Archive. Emma
Colby produced the cover layout with QuarkXPress 4.1 using Adobe's ITC Garamond font.
Robert Romano and Emma Colby designed the quick reference card using Adobe's Myriad
Condensed and ITC Garamond fonts.
David Futato designed the interior layout. This book was converted to FrameMaker 5.5.6
with a format conversion tool created by Erik Ray, Jason McIntosh, Neil Walls, and Mike
Sierra that uses Perl and XML technologies. The text font is Linotype Birka; the heading
font is Adobe Myriad Condensed; and the code font is LucasFont's TheSans Mono
Condensed. The illustrations that appear in the book were produced by Robert Romano and
Jessamyn Read using Macromedia FreeHand 9 and Adobe Photoshop 6. The tip and
warning icons were drawn by Christopher Bing. This colophon was written by Ann
Schirmer and Brian Sawyer.
The online edition of this book was created by the Safari production group (John Chodacki,
Becki Maisch, and Madeleine Newell) using a set of Frame-to-XML conversion and
cleanup tools written and maintained by Erik Ray, Benn Salter, John Chodacki, and Jeff
Liggett.
www.it-ebooks.info
"Build succeeded"
"Currency Converter" application
assembling component parts of
testing
"Document Print" application
"Dot View" application
About Box, customizing for
connecting controls to
creating component parts of
finishing touches for
help files, adding to
"Favorites" application
"freeze-drying" GUIs
"Hello, World" example
code for
GUI for
"Image Bundle" application
"Menu" application
coding/archiving, adding to
formatter, adding to
table-data sorting capability, adding to
"RTF Edit" application
"Simple Date" application
"Simple Inspector" application
"Simple Text Edit" application
"text editor" application
+ plus sign
- minus sign
.dmg files
.m files (Objective-C source code)
for Dot View[m files
Dot View
/* ... */ indicating comments
// indicating comments
= assignment operator 2nd
www.it-ebooks.info
== equality operator
%@ format token (print as object)
%d or %i (print as signed decimal)
%o (print as unsigned octal)
%s (print as string)
%u (print as unsigned decimal)
%x (print as unsigned hexadecimal)
@"..." construct
\ backslash
\: colon
About Box, customizing
acceptFirstResponder method (NSResponder class)
accessor methods
action messages
actions
connecting button to
Convert action defined for Controller class
defining
making connections and
target/action relationship
ADC (Apple Developer Connection)
addObject method\: (NSMutableArray class)
Address Book API
Adobe Photoshop, designing icons with
Adobe Portable Document Format (PDF)
alert dialog boxes
alert sheets
aligning objects
allKeys method (NSDictionary class)
alloc message, coupled with an init message
alloc method 2nd
alpha
appendString\: method (NSMutableString class)
Apple Computer
Human Interface Guidelines
partnering with
Apple Developer Connection (ADC)
Apple Help 2nd
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
CG calls
changes, tracking with notifications
character attributes 2nd
Cheetah release of Mac OS X
class methods 2nd
Class type
classes
creating
defining
for Currency Converter
for Dot View
implementing
for Currency Converter
for Dot View
protocols and
root class and
support
using
Classes group
Classic environment
CMYK
Cocoa
colors and
documentation/resources for further reading
frameworks of 2nd
text system of
web sites about
Cocoa Browser documentation viewer
CocoaTopics.html file 2nd
codenames and cats
coding
collection classes
storing collections as files
collections
colon (:), in method names/message arguments
colors 2nd
ColorSync 2nd
Column Title field, vs. Identifier field
www.it-ebooks.info
www.it-ebooks.info
creating
classes
custom views
data source for table views
delegates
GUIs
for table views example
Inspector panel
objects 2nd
projects in Project Builder
sample applications
\Simple Date
Currency Converter
for displaying date and time
Document Print
Dot View
Favorites
Hello, World 2nd
Image Bundle
Menu
RTF Edit
Simple Inspector
Simple Text Edit
View Print
Credits.rtf file
cString method (NSString class)
custom views
adding to main window
autosizing
paths, drawing into
preparing to draw into
strings, drawing into
CVS (Concurrent Version System)
data functionality
data source class, declaring for table views
data source, creating for table views
data types for text
data values
www.it-ebooks.info
www.it-ebooks.info
printing from
template for
document controller
document objects
document type arrays
documentation 2nd
accessing via Project Builder
print-on-demand
Documentation group
documents
document controller for
implementation for
speaking contents of
DotView class
creating
defining
DotView.h header file, defining
DotViewIcon.psd file
drag-and-drop
drawRect\: method (NSView class)
implementing
for Dot View
printing and
drawRect\: method (NSView class)[drawRect\: method (NSView class)
dynamic typing
embedded images
Empty interface window (Interface Builder)
encodeWithCoder\: method (NSCoding protocol)
entries (in dictionaries)
equality operator (==), vs. assignment operator (=)
error messages
event cycle
event messages
events
routing
types of
examples [See sample applications sample code]
exceptions
www.it-ebooks.info
www.it-ebooks.info
global resources
Gosling, James
grayscale
Groups & Files list (Project Builder)
groups, projects and
GUIs (graphical user interfaces) 2nd [See also user interface]
"freeze-drying"
adding buttons to
applications with vs. without
archiving
concepts in for Cocoa
creating
with Interface Builder
development tools for (list)
finishing touches for
vs. interfaces
table views example and
testing
header (.h) file 2nd
for Dot View
help command
help files
HIG (Human Interface Guidelines)
Horn, Max
HSB
HTML format, help files and
Human Interface Guidelines (HIG)
I/O management, Foundation framework and
.icns file
IconComposer utility
icons for applications
id keyword, dynamically typed outlet declarations and
id type
identifier attributes
Identifier field, vs. Column Title field
images
application icons and
www.it-ebooks.info
embedded
preloading
imaging
immutability vs. mutability
immutable collection classes
immutable strings
IMP type
implementation
implementation (.m) files 2nd
for Dot View
info panels
info property list
modifying
Info window
implementing code for
Info.plist file
informal protocols
inheritance
caution with
init message, coupled with alloc message
init method
invoking for arrays
initial first responder, setting
initialFirstResponder method (NSWindow class)
initializers
initWithCoder\: method (NSCoding protocol)
initWithContentsOfFile\: method (NSString class)
initWithFrame\: method (NSView class)
implementing
input/output [See entries at I/O]
insertObject\:atIndex\: method (NSMutableArray class)
insertString\:atIndex\: method (NSMutableString class)
Inspector panel, creating
inspector, sample application for
instance methods
instance, creating for Controller and Model classes
integration of graphical elements, Foundation framework and
interface
www.it-ebooks.info
www.it-ebooks.info
architecture for
functionality of, Foundation framework and
localizedStringForKey\:value\:table\: method (NSBundle class)
lockFocus method (NSView class)
locks
loops 2nd
.lproj directory extension
.m files (Objective-C source code) 2nd 3rd
Mac OS X
development documentation for
key environments of
language preferences for
Release 10.2 (Jaguar)
Software Update tool, Developer Tools and
MacOS bundle directory
mailing lists
main nib file
formatted cell example and
opening in Interface Builder
main window
adding custom views to
panels and
main.m file 2nd 3rd
manpages
margins, setting
memory leaks
memory management 2nd
multiple documents and
notifications and
rules for
Menu bar (Interface Builder)
menu items
document controller and
finishing touches for
message arguments
message expressions
message log
www.it-ebooks.info
messages 2nd
nested
parts of
method name
methods 2nd
calling
overriding
minus sign (-) in method definitions
model class [See Converter model class]
Model-View-Controller [See MVC]
models 2nd
adding entries to
mouse clicks 2nd [See also events]
mouseDown\: method, implementing for Dot View
mouse events
MP3 players
songs illustrating classes
multilingual applications
multiple-document architecture
multiple-inheritance
mutability vs. immutability
mutable arrays
mutable collection classes
mutable dictionaries
mutable strings
MVC (Model-View-Controller)
designing applications with
text system and
"MyDocument" class
implementing
nested messages
New Project Assistant (Project Builder)
NeXT Interface Builder
nextKeyView outlet
NeXTSTEP
Nib file window (Interface Builder)
nib files 2nd
localizing
www.it-ebooks.info
main
nil/Nil type
notification center
notifications 2nd
tracking changes with
NS prefix
NSApp object
event handling and
NSApplication class 2nd
event cycle and
NSApplicationMain function
NSArchive class
NSArray class
NSBeginAlertSheet function
NSBundle class
resources-handling methods and
NSCoder class
NSCoding protocol, serialization and
NSColor class
NSDictionary class
NSDocument class
file wrappers and
skeletal implementation of
window controllers and
NSDocumentController class 2nd
NSEvent class
NSFontManager class
NSFontPanel class
NSKeyValueCoding protocol
NSLayoutManager class
NSLog function 2nd
NSMutableArray class 2nd
NSMutableDictionary class
NSMutableSet class
NSMutableString class
NSNotificationCenter class
NSNumber class
NSObject class
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
ports
preferences
accessing from the command line
functionality of, Foundation framework and
language, in Mac OS X
overriding
Preferences folder, caution when editing files in
preloading images
Preview application
primitive types
print command 2nd
print info objects
print-object command 2nd
print\: message
printf (C programming language)
printf manpage, accessing
printing 2nd 3rd
print operations for
tokens for
printShowingPrintPanel\: method (NSDocument class)
process ID number (PID) 2nd
Products group 2nd
programming, Foundation framework and
Project Builder
accessing documentation via
autoindentation for
viewing application icons and
projects
creating/building
naming
properties
application-wide, setting
property lists (plist files)
protocols 2nd 3rd
types of
Puma release of Mac OS X
Pure Java, as project type
Quartz environment 2nd
www.it-ebooks.info
www.it-ebooks.info
routing events
RTF files, Project Builder and
RTF format
RTFD format 2nd
run loops 2nd
runtime
sample applications [See also Foundation Tools, creating]
\Simple Date
Currency Converter
for displaying date and time
Document Print
Dot View
Favorites
finishing touches for
Hello, World
code for
GUI for
Image Bundle
Menu
RTF Edit
Simple Inspector
Simple Text Edit
View Print
sample code
"Hello, World" example 2nd
custom views, setting up (finalizing)
dealloc method
designated initializer method
dot, changing size of
DotView.h header file
implementation file
for Dot View
interface, drawing
key-value coding
mouse-down events
MyDelegate.m file
modified to use sheets
notification center, removing object from
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
www.it-ebooks.info
key/main
onscreen/offscreen
resizing
window controllers for
windowShouldClose\: method 2nd
writeToFile\: method (NSString class) 2nd
zero-based indexing