All The Quickies
<img src=/images/eyes/eyes7.jpg width=111 height=75 align=right>
<li> <b>Changing the 'screen' hotkey</b> (Unix->Tools)<br>
Running <code>emacs</code> inside of <code>screen</code> is made difficult because both want to use C-A for something important. C-O (AFAIK so far) isn't too useful for my use of emacs, so I use that for screen now:
<pre>screen -e^Oo emacs</pre><p>
<li> <b>Rejoining a disconnected screen session</b> (Unix->Tools)<br>
<pre>screen -r</pre><p>
<li> <b>Backups on the cheap</b> (Unix->General)<br>
I've been using <a href="http://www.geocities.com/paulotex/tar/">hfstar</a> for doing backups. I tar directory hierarchies to an external firewire drive from my TiBook (say my entire <code>$HOME</code> directory, plus the </code>/Applications</code> directory. There's probably other stuff that should be snarfed (like <code>/Library</code>), but I didn't on my last backup, and doing a restore from scratch seemed to work OK. Here's a quick outline of backing up to the external firewire drive called "Wikkit":
<ul>
<li> <code>% sudo mkdir /Volumes/Wikkit/backup</code>
<li> <code>% cd /Users</code>
<li> <code>% sudo hfstar cf - bork | (cd /Volumes/Wikkit/backup; sudo hfstar xf -)</code>
</ul>
The tar above is the standard push-pull tar. Both sides are run as root so that owner, group, and permissions are preserved for the files.<p>
To Restore:
<ul>
<li> <code>% cd /Users</code>
<li> <code>% sudo mv bork bork-orig</code>
<li> <code>% cd /Volumes/Wikkit/backup</code>
<li> <code>% sudo hfstar cf - bork | (cd /Users; sudo hfstar xf -)</code>
</ul>
<p>
Some folks have had success with <a href="http://www.dan.co.jp/cases/macosx/backup-volume.html">psync</a>, but we haven't used it yet.<p>
<li> <b>Case-insensitive file completion with bash</b> (Unix->General)<br>
If you use bash as your shell and use filename completion a lot, you can get bash to ignore the case of filenames just like HFS does. Just put this in your <code>~/.inputrc</code> file:
<p>
<code>set completion-ignore-case On</code>
<p>
Then when you type <code>ls /appli<tab></code> in the shell you'll get </code>ls /Applications/</code> like you ought to!<p>
<li> <b>Changing all *.oop files to *.ack</b> (Unix->General)<br>
<code>% ~/bin/<a href="files/rename.pl">rename.pl</a> 's/.oop$/.ack/' *.oop</code>
<p>
To tack on an extension to all files, do something like
<code>% ~/bin/<a href="files/rename.pl">rename.pl</a> 's/(.*)/$1.jpg/' *</code><p>
<li> <b>Converting from DOS to unix line terminators</b> (Unix->General)<br>
<code>% perl -pi -e 's/\r/\n/g' mealcsv.csv</code><p>
<li> <b>Converting mac newlines to unix newlines in a text file</b> (Unix->General)<br>
<pre>
tr '\r' '\n' < macfile.txt > unixfile.txt
</pre><p>
<li> <b>Creating a disk image from the command line</b> (Unix->General)<br>
<pre>% hdiutil create -ov -imagekey zlib-level=9 -fs HFS+ -format UDZO -scrub -srcfolder /Path/To/The/Goodies fooby.dmg</pre><p>
<li> <b>Daemontools controls</b> (Unix->General)<br>
<code># svc -d /service/whatever</code> - Bring the server down<p>
<code># svc -u /service/whatever</code> - Start the server up and leave it in keepalive mode.<p>
<code># svc -o /service/whatever</code> - Start the server up once. Do not restart it if it stops.<p>
<code># svc -t /service/whatever</code> - Stop and immediately restart the server.<p>
<code># svc -k /service/whatever</code> - Sends the server a KILL signal. This is like KILL -9. If svc -t fails to fully kill the process, use this option.
<p>
(Thanks to the OpenACS documentation)<p>
<li> <b>Dealing with directories with spaces in them</b> (Unix->General)<br>
You can either escape the directory name with a space:
<code>% cd /Users/bork/Library/Screen\ Savers</code>
<p>
or you can surround it with quotes:
<code>% cd "/Users/bork/Library/Screen Savers"</code>
<p>
Note that tilde expansion (<code>~/Library</code>) is suppressed if you use the quotes.<p>
<li> <b>Deatching another screen session.</b> (Unix->General)<br>
Sometimes if you have an unclean disconnect, screen can still be attached and you get the smackdown:
<pre>% <b>screen -r</b>
There is a screen on:
8395.pts-0.vikki (Multi, attached)</pre>
You can boot off that other loser by using:
<pre>% <b>screen -d 8395.pts-0.vikki</b></pre>
and then doing <code>screen-r again</code><p>
<li> <b>Directory space consumption</b> (Unix->General)<br>
To see how much disk space a directory consumes, use<br>
<code>% du -sk dir_name</code><p>
To see how much each of a directory's contents consume, use<br>
<code>% du -sk dir_name/* | sort -n</code>
to get a list ordered by size.<p>
<li> <b>Display the info.plist embedded in a compiled file</b> (Unix->General)<br>
<pre>% otool -s __TEXT __info_plist ./thebinary | xxd -r</pre>
<p>
Muchos Thankos to <a href="http://red-sweater.com">Dan Jalkut</a> for this one.<p>
<li> <b>Doing something to a hierarchy of files</b> (Unix->General)<br>
To do something to all of the html files in a directory (e.g. to grep something) <br>
<code>% cd directory<br>
% find . -name "*.h" -exec grep NSDoWhatIMean {} ; -print</code><br>
Don't forget the dot right after find, and the <code>{}</code> construct. Also make sure there's a space after the <code>{}</code> and <code>;</code><p>
<li> <b>Don't show file names with a multi-file grep</b> (Unix->General)<br>
use the <code>-h</code> flag:
<pre>% grep -h chicken ~/Documents/ircLogs/FreeNode/2007*/#macsb*</pre><p>
<li> <b>Find all the literal strings in an objc source file</b> (Unix->General)<br>
<pre>
egrep -h -o '@"[^"]*"' MJFirstViewController.m
</pre>
The <tt>-h</tt> suppresses printing the file name, <tt>-o</tt> is "Prints only the matching part of the line"<p>
<li> <b>Find files larger than 2 gigs</b> (Unix->General)<br>
<pre>find /Volumes/Vikki/ -type f -size +2G -print</pre><p>
<li> <b>Finding Mac OS X version from the command line</b> (Unix->General)<br>
<code>% sw_vers</code>
<pre>
ProductName: Mac OS X
ProductVersion: 10.3.7
BuildVersion: 7S215
</pre><p>
<li> <b>Finding parent process IDs on Linux</b> (Unix->General)<br>
<code>% ps -eo "%p %P %c" </code><p>
<li> <b>Finding suid executables</b> (Unix->General)<br>
To find setuid executables, run<p>
<code>% find . -type f -perm +06000 -print</code>
<p>
(or use <code> find / </code> to start looking from the root directory.)<p>
<li> <b>Finding the path corresponding to an open file descriptor</b> (Unix->General)<br>
With <code>fcntl</code>, all things are possible.
<pre>
char pathbuf[PATH_MAX];
if (fcntl(fd, F_GETPATH, pathbuf) >= 0) {
printf ("wow: %s\n", pathbuf);
} else {
printf ("oops %s\n", strerror(errno));
}
</pre>
Finding this feature was a surprise, having lived under the assumption that you can't map from a file descriptor to a path in the file system. Obviously things like sockets and pipes will return an error, but it actually seems to work.<p>
<li> <b>Getting a count from grep</b> (Unix->General)<br>
To see how many times "fix me" occurs in the Objective-C files in the current directory:<br>
<code>% grep -c "fix me" *.m</code><p>
<li> <b>Getting curl to download a file</b> (Unix->General)<br>
<code>% curl -O http://borkware.com/quickies/files/fullscreensaver.tar.gz</code><p>
<li> <b>Getting grep to return file names of matches rather than the actual match</b> (Unix->General)<br>
Use the <code>-l</code> (dash ell) flag to grep. If a file contains the expression, the name of the file (rather than the matching line) is displayed.
e.g. to open all the files that contain 'rzolf'<p>
<code>% grep -l rzolf *.ham | xargs open</code><p>
<li> <b>Grepping for flags</b> (Unix->General)<br>
Sometimes you want to grep for something like "-H", but don't want it to be interpreted as a grep flag. The <code>-e</code> option to the rescue:
<pre>
% grep -e -H filename
</pre><p>
<li> <b>Interleaving the contents of two files</b> (Unix->General)<br>
Say file1.txt had the contents
<pre>
hoover
Greeble
NI POP
</pre>
and file2.txt had the contents
<pre>
bork
Bork
BORK
</pre>
Interleave them with
<pre>
% paste -d '\n' file1.txt file2.txt > blarg
</pre>
and get
<pre>
hoover
bork
Greeble
Bork
NI POP
BORK
</pre><p>
<li> <b>Mounting a disk image from the command line</b> (Unix->General)<br>
<code>% hdiutil mount diskimage.dmg</code><p>
<li> <b>Perl breaks in weird and wonderful(?) ways</b> (Unix->General)<br>
Perl 5.8 uses unicode internally by default, which apparently causes trouble for some big perl packages (like SpamAssassin's makefile getting totally trashed when you create it). Set the <code>LANG</code> environment variable to be <code>en_US</code> to make it happier.<p>
<li> <b>Prevent deep sleep</b> (Unix->General)<br>
My lappy has 16 gigs of memory (yay me), and when the machine goes in to a deep sleep / hibernation, it effectively powers down. Upon wake, memory has to be read back in from disk, which can take a noticeable amount of time. While that's happening, the screen is blank, and the computer is inert like it has totally died.
<p>
Once you've totally finished panicking and starting your recovery plan ("dammit now I'll lose a day's work getting this to a genius and re-cloning my repos to another machine") the memory has finished swapping in and the machine wakes. "Hi! Now that you're completely carbonated, let's get back to work!"
<p>
Unsurprisingly, major OS updates seem to reset this value. (Firmware updates may do it too.)
<p>
(This may be incomplete - I have a couple of days still to make sure it totally works)
<p>
Turn off hibernation with:
<pre>
# sudo pmset -a standby 0
# sudo pmset -a hibernatemode 0
</pre><p>
<li> <b>Preventing an app from opening stale windows</b> (Unix->General)<br>
Lion introduced the idea that apps restore all their windows that were last open. Sometimes that's wonderful. For some apps, it's maddening. You can tweak individual apps by setting a default:
<pre>
% defaults write com.bignerdranch.bigshow NSQuitAlwaysKeepsWindows -bool false
</pre>
(thanks to Step Christopher for this one)<p>
<li> <b>Pruning directories with find</b> (Unix->General)<br>
Sometimes you don't want find to go into a particular subtree. This command finds all swift files from the current directory, except those in <code>experimental</code>:
<pre>
% find . -path ./experimental -prune -o -name "*.swift"
</pre><p>
<li> <b>Removing files with a leading "-"</b> (Unix->General)<br>
<code>% rm -- -i-am-a-bad-file</code><p>
<li> <b>Removing quarantine xattribute from downloaded files.</b> (Unix->General)<br>
Download a zip file full of graphics, and they all have extra attributes?
<pre>
% <b>ls -l</b>
total 18192
-rw-r--r--@ 1 markd staff 13354 Jan 5 18:46 Icon-114.png
-rw-r--r--@ 1 markd staff 21379 Jan 18 10:56 Icon-152.png
-rw-r--r--@ 1 markd staff 484470 Jan 5 18:40 Icon-167.png
...
</pre>
Which are quarantine tags:
<pre>
% <b>ls -l@</b>
total 18192
-rw-r--r--@ 1 markd staff 13354 Jan 5 18:46 Icon-114.png
com.apple.quarantine 61
-rw-r--r--@ 1 markd staff 21379 Jan 18 10:56 Icon-152.png
com.apple.quarantine 61
-rw-r--r--@ 1 markd staff 484470 Jan 5 18:40 Icon-167.png
com.apple.quarantine 61
</pre>
Get rid of them with this:
<pre>
% <b>xattr -d com.apple.quarantine *</b>
</pre><p>
<li> <b>Replacing a string in a set of files</b> (Unix->General)<br>
<code>% perl -pi.bak -e 's/OLDSTRING/NEWSTRING/g' *.html</code><br>
This will leave "*.bak" files around of the original version, in case the replacement string didn't quite do what you wanted.<p>
<li> <b>Restricting Find to only deal with files (not directories)</b> (Unix->General)<br>
<code>% find . <b>-type f</b> -name "*.html" -exec grep Spoon {} ; -print</code><p>
<li> <b>Rsyncing a directory hierarchy from one machine to another</b> (Unix->General)<br>
I'm moving a bunch of movie files from one machine to another. I used this rsync command to copy it from the remote machine to the local one to the directory <code>/Volumes/Media2</code>
<pre>
% rsync -avz -e ssh --progress 'biggun.local:/Volumes/Video/AppleTV\ Videos/Exercise' /Volumes/Media2
</pre>
I can never remember what the flags mean, so:
<ul>
<li> -a : archive mode (recursive, also preserve symlinks, permissions, owner and group)
<li> -v : verbose
<li> -z : compress for transport
<li> -e ssh : use ssh for the transport pipeline
<li> --progress : see progress info
</ul><p>
<li> <b>Running a pipeline in sudo</b> (Unix->General)<br>
<code>% sudo sh -c "tar cf - * | (cd /mirror4/web; tar xf -)"</code><p>
<li> <b>Sanity checking an XML file</b> (Unix->General)<br>
<code>% xmllint -noout kipple.xml</code>
Handy for a quick check after hand-editing a file.<p>
<li> <b>Seeing all the flags gcc takes</b> (Unix->General)<br>
<code>% gcc -v --help</code><p>
<li> <b>Seeing extended attributes with 'ls'</b> (Unix->General)<br>
Sometimes you see an @ after the directory permissions in an <code>ls -l</code>:
<pre>% ls -l ~/junk/SnapshotRepository.sparseimage
-rw-r--r--@ 1 markd markd 135270400 Jul 24 20:38 /Users/markd/junk/SnapshotRepository.sparseimage</pre>
That means there's some extended attributes. use <code>ls -l@ to see them</code>:
<pre>% ls -l@ ~/junk/SnapshotRepository.sparseimage
-rw-r--r--@ 1 markd markd 135270400 Jul 24 20:38 /Users/markd/junk/SnapshotRepository.sparseimage
com.apple.diskimages.fsck 20 </pre><p>
<li> <b>Seeing lines that exist in two files</b> (Unix->General)<br>
Assuming the files are sorted, you can use <code>comm</code>.
<p>
Here I'm wondering if any objc files in the current directory also exist in the Classes subdirectory
<pre>
% ls -1 *.[hm] *.xib | sort > oopack
% cd Classes
% ls -1 *.[hm] *.xib | sort > ../oop2
% cd ..
% <b>comm -12 oopack oop2</b>
</pre>
The <code>-12</code> suppresses lines only in file1, and lines only in file2. I'm more interested in their intersection.<p>
<li> <b>Seeing the console.log without running Console.app</b> (Unix->General)<br>
In a terminal or an emacs shell:<br>
<code>% tail -f /var/tmp/console.log</code><p>
<li> <b>Seeing the files in a zip archive</b> (Unix->General)<br>
<code>% unzip -l <i>snorgle.zip</i></code><p>
<li> <b>Seeing the parent process id in ps</b> (Unix->General)<br>
Use the <code>ppid</code> option with the <code>-o</code> flag:
<p>
<code>% ps -axo user,pid,<b>ppid</b>,vsz,tt,state,start,time,command
</code><p>
<li> <b>Silencing Macbook power chime</b> (Unix->General)<br>
The modern-day flutterby keyboard Macbooks make a soothing (?) chime when plugged in to power, even if the system volume is muted. If you don't want to hear that, you can do
<pre>
% defaults write com.apple.PowerChime ChimeOnNoHardware -bool true
% sudo killall PowerChime
</pre><p>
<li> <b>Sort grep-c</b> (Unix->General)<br>
<tt>grep -c</tt> will give you the number of hits for the given pattern in each file. It'd be nice to sort them, but the count occurs after the filename, e.g. <tt>GreebleBork:23</tt>
<p>
So, trick <tt>sort</tt> into using the last field (with a colon separator) as the sort key:
<pre>
% grep -c translatesAuto *.xib | sort -t: -n -k2
</pre><p>
<li> <b>Toggle 'screen's visible bell on and off</b> (Unix->General)<br>
Assuming <code>^t</code> is the hotkey, doing <code>^t ^g</code> will toggle the visible bell on and off.<p>
<li> <b>Turning blobs of json in to something somewhat readable</b> (Unix->General)<br>
yay prettyprinting!
<pre>
% cat some-unformatted-manifest.json | python -m json.tool
</pre>
(courtesy of Chris Donnelly)<p>
<li> <b>Using "find" with multiple -name clauses</b> (Unix->General)<br>
<code>% find . \( -name "*.jpg" -or -name "*.gif" \) -exec do_something_with {} ;</code><p>
<li> <b>Using Image Magick to scale an image proportionally</b> (Unix->General)<br>
<code>% find . -name "*.jpg" -exec convert -verbose -geometry 150x150 {} {} ;</code><br>
This converts the files in-place, so have a backup. Even though the geometry is specified as 150x150, that just means make the image no larger than 150 in either direction.<p>
<li> <b>Using ImageMagick to get the dimensions of a file</b> (Unix->General)<br>
<code>% identify filename.jpg</code><p>
If you want to be studly, you can use <code>-format</code> to tailor
the output<p>
<code>% identify -format "insert into bw_graphics values (generate_primary_key(), '/images/llamas/%f', %w, %h);" *.jpg</code><br>
generates a sql <code>insert</code> statement for each image.<p>
<li> <b>Using awk to extract the n'th column</b> (Unix->General)<br>
<code>nm</code> outputs a three-column format. I needed to get the set of defined symbols (in particular those that start with <code>__</code>, in tracking down a C++ linking problem), which is the third column. <code>awk</code> is good for that. Use <code>$N</code> to get the nth column (zero-index). This pipeline did the trick:
<p>
<pre>
nm ./oopack.o | awk '{print $2}' | sort | grep __
</pre>
(Thanks to DougEDoug for the pointer)<p>
<li> <b>Using awk to print fields</b> (Unix->General)<br>
Here's a way to have awk print all fields (separated by spaces) from each line, except for fields number 1 and 9 (starting with a count of 1):
<pre>% awk '{ for (f=1; f <= NF; f++) { if (f != 1 && f != 9) printf("%s ", $f);} printf("
");}' < ook.txt</pre>
Or you can put this at the end of a pipeline instead of directing a text file into the command. (courtesy of DougEDoug)<p>
<li> <b>Using tar to copy across the network</b> (Unix->General)<br>
<code>% tar cf - ./stuff | ssh theOtherMachine "tar xf -"</code><p>
<li> <b>cd to directory with a space in its name</b> (Unix->General)<br>
There's two ways to do it. The first is to backslash-escape the spaces:<br>
<code>% cd /Users/bork/development/cool\ stuff/neat\ things</code>
<p>
The other is to use shell completion to put in the backslashes for you. In
the above path, if you type<br>
<code>% cd /Users/bork/development/cool<b>[tab]</b></code>, it'll expand
"<code>cool stuff</code>" for you.<p>
<li> <b>safely copying directories</b> (Unix->General)<br>
<code>cp -R</code> is generally unsafe to copy directories with since it will copy through symbolic links rather than just copying the link (newer <code>cp</code>'s have an -H flag that will work around this). The classic unix way of doing things like this is a push-pull <code>tar</code>:<p>
<code>% tar cf - dir-name | (cd /destination-dir; tar xf -)</code>
<p>
In English: tar out through a pipe into a subshell that's changed directories, feeding that pipe into the tar extraction. If you run both sides of the pipeline as root (via <code>sudo</code>), the file owners and permissions will be preserved (which <code>cp</code> won't do)<p>
<li> <b>tar + xargs for huge numbers of files</b> (Unix->General)<br>
If you have too many files to add to a tarball, you can run into the shell's command limitation. If you use xargs + tar cf, you'll not get all of your files added to the tarball. Use tar's <code>rf</code> command to append to an archive rather than creating one:
<pre>
% find . -name "*.yuck" -print0 | xargs -0 tar rvf oop.tar
</pre>
<code>-print0</code> (zero) tells find to output the filenames with trailing NUL characters (rather than newlines), and <code>-0</code> tells xargs to use a NUL as its input separator, rather than newlines/spaces. The upshot is that you can handle filenames with spaces in them.
<p>
Thanks to Ben Cox for the -print0 / -0 suggestion.<p>
<li> <b>using ssh (or CVS) without a password</b> (Unix->General)<br>
Suppose you have two machines, the local one (your laptop) and the remote one (the honking big server located in Minneapolis that hosts gitdown.com) To ssh (or CVS) from the laptop to the server without needing a password, perform these steps:
<ol>
<li> <code>% ssh-keygen -t rsa</code><br>
(don't use a passphrase. Just hit return twice)<p>
<li> <code>% scp ~/.ssh/id_rsa.pub gitdown.com:</code><p>
<li> On the remote machine (gitdown)<br><p>
<code>% cat ~/id_rsa.pub >> ~/.ssh/authorized_keys</code>
</ol><p>
<li> <b>I can't move or symlink a directory</b> (Unix->Hacks)<br>
If you get the error <pre>mv: rename build/ to oopack: Is a directory
</pre> when using <code>mv</code> or <code>ln</code> on the command line, that's a Darwin error. To correct the problem, remove the trailing slash from the directory name.<p>
<li> <b>Printing network traffic</b> (Unix->Hacks)<br>
<pre>% sudo tcpdump -Atq -s 0 -i en1</pre>
<code>-i en1</code> will display traffic on your airport card. Use <code>en0</code> (or nothing, for most systems) for built-in ethernettage.
<p>
You can add a host line to limit output to a particular host
<pre>% sudo tcpdump -Atq -s 0 -i en1 host zombo.com</pre>
(thanks to Dan Jalkut for this one)<p>
<li> <b>Restarting a crashed Dock</b> (Unix->Hacks)<br>
Ever be using your machine and you can't command-Tab and your Dock is gone? And there's no Dock process to kill? (#itJustWorks). You can restart the Dock with:
<pre>
% launchctl stop com.apple.Dock.agent
% launchctl start com.apple.Dock.agent
</pre><p>
<li> <b>Unwedging stuck boots</b> (Unix->Hacks)<br>
If I go longer than a week or so between reboots, it (Mavericks) literally takes forever (I counted) for it to actually boot. Verbose mode shows fsck runs (and actually completes, but it hangs.) If that happens to you regularly, try this:
<pre>
// Boot into single-user mode with command-S on power-on
# /sbin/fsck -fy
# /sbin/mount -uw /
# mv /var/folders /var/folders-dorked
# reboot
</pre>
Nearly instant boot again.<p>
<li> <b>Changing a file's modification date</b> (Unix->Random)<br>
Sometimes you're doing work which uses a file's modification time to decide whether you want to do something (like don't process today's log file, or remove a really old file). You could use a time machine to test this, or use <code>touch</code>. You can specify a specific date or time using the format <code>[[CC]YY]MMDDhhmm[.SS]]</code>. Here's an example with alternating bolds to make it easier to read:
<pre>
% touch -t 20<b>07</b>03<b>14</b>15<b>36</b> snork.waffle
% ls -l snork.waffle
-rw-r--r-- 1 markd markd 298 Mar 14 15:36 snork.waffle
</pre><p>
<li> <b>Creating a RAM Disk</b> (Unix->Random)<br>
<code>% hdid ram://size</code>
where size is the size in bytes. the system will round that number up or down as it sees fit.<p>
<li> <b>Getting OS version from the command line</b> (Unix->Random)<br>
<pre><b>% sw_vers -productVersion</b>
10.5.1
</pre><p>
<li> <b>Seeing media types supported by your CD/DVD burner</b> (Unix->Random)<br>
On Panther, you can run <code>/usr/bin/drutil info</code> to get a listing of the kinds of media your drive supports. For example, on the 17" iLamp:<pre>
% <b>drutil info</b>
Vendor Product Rev
PIONEER DVD-RW DVR-104 A227
Interconnect: ATAPI
SupportLevel: Apple Shipping
Cache: 2000k
CD-Write: -R, -RW, BUFE, Test, IndexPts, ISRC
DVD-Write: -R, -RW, BUFE, Test
Strategies: CD-TAO, CD-SAO, DVD-DAO
</pre><p>
<li> <b>Setting file system attribute flags</b> (Unix->Random)<br>
<code>chflags</code> in Mac OS X works like <code>chattr</code> over in Linux-land. So to make a file immutable (can't change it, even if you're the superuser), you can <code>sudo chflags uchg file-name(s)</code>. To undo it, do <code>sudo chflags nouchg file-name(s)</code><p>
<li> <b>Adding a new machine alias via netinfo(e)</b> (Unix->Administration)<br>
The NetInfo Manager utility is painfully slow. Here's how to add a machine via the command line. In this case adding the IP for a machine called "tower" (thanks to rzolf)
<pre>
# niutil -create . /machines/tower
# niutil -createprop . /machines/tower ip_address 10.0.1.155
# niutil niutil -createprop . /machines/tower serves ./local
</pre><p>
<li> <b>Increasing shared memory limits</b> (Unix->Administration)<br>
Edit <code>/System/Library/StartupItems/SystemTuning/SystemTuning</code><p>
tweak some values, like<pre>
sysctl -w kern.sysv.shmmax=167772160 # bytes: 160 megs
sysctl -w kern.sysv.shmmin=1
sysctl -w kern.sysv.shmmni=32
sysctl -w kern.sysv.shmseg=8
sysctl -w kern.sysv.shmall=65536 # 4k pages: 256 megs
</pre>
Save, and restart your system.
<p>
On some Panther seeds, you may need to add these things to <code>/etc/rc</code><p>
<li> <b>Updating critical system files while preserving the original timestamp</b> (Unix->Administration)<br>
Sometimes you need to edit a file, like <code>/etc/rc</code>, but you want to be able to back out your changes. You also want to preserve the original timestamp of the file. That way if you back out your change, someone else coming along doesn't have to figure out "why did <code>/etc/rc</code> change yesterday, it doesn't look any different?" Here's how:
<ol>
<li> Move (<b>not</b> copy) the original to a backup name. This will preserve the timestamp:
<code>% sudo mv rc rc-orig</code><p>
<li> Copy the original to the usual name:
<code>% sudo cp rc-orig rc</code><p>
<li> make your edits:
<code>% sudo vi rc</code>
</ol>
If you decide your changes aren't worth keeping, you would move the original back:<br>
<code>% sudo mv rc-orig rc</code><p>
Thereby undoing your changes, and not messing up the timestamp of the original file.
<p>
(mucho thankos to Louis Bertrand for this one)<p>
<li> <b>Bizarro Error message</b> (Tools->General)<br>
gcc gave me this error:<br>
<code>cc1obj: error: type '<built-in>' does not have a known size</code><br>
without any line number to give a clue what was going on. The error turned out to be having an objective-C method with an argument type of <code>(void)</code>, a typo that should have been <code>(void *)</code>.<p>
<li> <b>Block that measures time.</b> (Tools->General)<br>
Sometimes you want to do a quick measurement of a chunk of code to see how long it takes, and you don't want to crank up <strike>gprof</strike> <strike>Shark</strike> Instruments to do it. <code>mach_absolute_time</code> is the finest-grained timer in the system. Here's a little utility to time a block:
<pre>
#import <mach/mach_time.h> // for mach_absolute_time() and friends
CGFloat BWTimeBlock (void (^block)(void)) {
mach_timebase_info_data_t info;
if (mach_timebase_info (&info) != KERN_SUCCESS) return -1;
uint64_t start = mach_absolute_time ();
block ();
uint64_t end = mach_absolute_time ();
uint64_t elapsed = end - start;
uint64_t nanos = elapsed * info.numer / info.denom;
return (CGFloat)nanos / NSEC_PER_SEC;
} // BWTimeBlock
</pre>
And you would use it like:
<pre> NSString *thing1 = @"hi";
NSString *thing2 = @"hello there";
time = BWTimeBlock(^{
for (int i = 0; i < LOOPAGE; i++) {
[thing1 isEqualTo: thing2];
}
});
printf ("equalTo time: %f\n", time);
</pre><p>
<li> <b>Changing a dylib's install name</b> (Tools->General)<br>
A dynamic library has an 'install name', which is where the library will assume it will be installed. For things like frameworks that will be stored in an application bundle, the install name has something like <code>@executable_path/../blah</code>. Sometimes you get a library from someone else (or something generated by a gigantic configure script) and need to change the install name. The <code>install_name_tool</code> will do that for you. Say I had a library, libbork.dylib, that will end up being placed side-by-side with the executable of a cocoa app, this is how I would fix it:
<pre>
% install_name_tool -id @executable_path/libbork.dylib ./libbork.dylib
</pre><p>
<li> <b>Resizing Windows in IB</b> (Tools->General)<br>
Resizing complex windows in IB can be a pain because the contents don't reflow when you change the window size.
<p>
<i>In Interface Builder 2:</i> If you hold down the control key while resizing, though, the contents obey their spring configurations and will resize accordingly. Makes the process a whole lot easier.
<p>
<i>In Interface Builder 3:</i> If you hold down command while resizing, the contents obey their spring configurations and will resize accordingly. (This avoids the annoying process of resizing each widget within the windows afterwards.) Holding option while resizing will display the pixel values of the padding. Holding command-option while dragging displays both.
<p>
(muchos thankos to Quinn Taylor at the <a href="http://cocoaheads.byu.edu/">BYU CocoaHeads</a> for the IB3 update)<p>
<li> <b>Seeing a lot of gcc's #defines</b> (Tools->General)<br>
<code>% gcc -E -dM -x c /dev/null</code>
<p>
(thanks to xmath on #macdev for this one)<p>
<li> <b>Seeing what frameworks an application links against</b> (Tools->General)<br>
<code>% otool -L /path/to/application.app/Contents/MacOS/application</code><p>
<li> <b>Weak Linking in a Plugin</b> (Tools->General)<br>
I've got some useful utilities in a .c file that I use for some debugging things. And I'm also writing a plugin that will go into an app I don't control. I also have a test harness that I can load the plugin and do effective debugging. This debugging file has some static variables that it uses to control things, like whether to emit log messages, or do other diagnoses.
<p>
The problem is, if I include the <code>usefulStuff.c</code> in my plugin and my test harness, there are two copies of the static variables, and the test harness can't control the plugin. If I don't include <code>usefulStuff.c</code> in the plugin I get link errors. If I don't declare as weak linked the functions I use, I'll get errors when the final program loads the plugin. Sucks to be me.
<p>
Here's one way of doing it (which is kinda hacky, but after spending a day inside of the ld man page, and other pain, the fact it works is good enough for me for right now).
<p>
<b>In the source file for the plugin that uses stuff from usefulStuff:</b>
<p>
Declare the function to be weak: <pre>extern void BWDebugLog (int blah, char *blah) __attribute__((weak_import));</pre>
(you can also do this in your header files. In this case, I didn't want to touch the header)
<p>
Before you use the function, make sure it's not <code>NULL</code>. Note there's no trailing <code>()</code> after the function name.
<pre>if (BWDebugLog != NULL) {
BWDebugLog (23, "stuff");
}</pre>
<b>In the Xcode project for the plugin</b>
<p>
Add the flags <code>-flat_namespace</code> and <code>-undefined dynamic_lookup</code> to the "Other Linker Flags" (a.k.a. <code>OTHER_LDFLAGS</code>). The flat namespace lets the symbols be found in the test harness when the plugin is loaded. The undefined dynamic_lookup means to suppress warnings about undefined symbols. This could conceivably mask errors, but it's no worse than ZeroLink.
<p>
<b>In the Xcode project for the test harness</b>
<p>
Add <code>usefulStuff.c</code>, and turn on the "Preserve Private External Symbols" checkbox (a.k.a <code>-keep_private_externs</code>). That turns the symbol <code>BWDebugLog</code> (and others) into exported symbols that'll be visible to the plugin. Otherwise the plugin will never get past that <code>!= NULL</code> check earlier.
<p>
Once all that's done, my plugin loads into the test harness and can get controlled. It can be loaded into the Real App without undefined symbol errors.<p>
<li> <b>Running a particular architecture</b> (Tools->Hacks)<br>
otest is kind of annoying that it tries to run the 64 bit version of your unit tests, especially if you don't have any. Typically you'd get an error like
<blockquote><code>2009-06-01 12:49:29.447 otest[70099:203] *** NSTask: Task create for path '/blah/blah/blah' failed: 8, "Exec format error". Terminating temporary process.</code></blockquote>
You can force otest to run a particular architecture with the arch command:
<pre>
% arch -arch i386 /Developer/Tools/otest build/Debug/Snoogle.octest
</pre><p>
<li> <b>wrong kind of tag compiler error.</b> (Tools->Random)<br>
This one stumped me for awhile:<br>
<code>BWNagType.h:23: `BWConcreteType' defined as wrong kind of tag</code>
<p>
In this particular case, I had a type I had declared with:<br>
<code>@class BWConcreteType;</code>
and later on I had decided to turn it into an enum:<br>
<code>typedef enum BWConcreteType { ... } BWConcreteType;</code>
<br>
without removing the previous <code>@class</code> declaration. So be on the look out for conflicting types for the same symbol.<p>
<li> <b>Building projects from the command line</b> (Project Builder->General)<br>
Look at <code>xcodebuild</code>. We've got a <a href="/rants/emacs-dev/">rant about cocoa development in emacs</a>, which includes a discussion of <code>pbxbuild</code>, the predecessor to <code>xcodebuild</code>. Some of the arguments have changed over the years. I usually run it like: <pre>
% xcodebuild -configuration "Debug" -target "My Groovy App"
</pre>
If you have one target, or have a default target, you can leave out the <code>-target</code> argument.<p>
<li> <b>Adding a pop up menu thingie</b> (General->General)<br>
On iOS, the menu you get when you long-press in a text field is called a UIMenuController, and you can pop one up for your own classes.
<p>
First, you need to be able to become first responder, and be able to perform the actions of the menu items you want displayed. The menu controller sifts through its default set, and actions your provide and sees if any can be performed. If not, they get dropped on the floor. This assumes that the menu being shown only has "Duplicate".
<pre>- (BOOL) canPerformAction: (SEL) action withSender: (id) sender {
return action == @selector(duplicate:);
} // canPerform
- (BOOL) canBecomeFirstResponder {
return YES;
} // canBecomeFirstResponer
</pre>
Then acquire an array of UIMenuItems (title / selector pairs), become first responder (that's important), and then figure out where you want the menu item to point to. Then show it:
<pre> // Permanent set of items. You might want to do something saner
static NSArray *s_menuItems;
if (s_menuItems == nil) {
UIMenuItem *item = [[UIMenuItem alloc] initWithTitle: @"Duplicate"
action: @selector(duplicate:)];
s_menuItems = [ @[ item ] retain];
[item release];
}
[self becomeFirstResponder]; // important!
// point the menu point somewhere
CGPoint point = CGPointMake (160.0, 5.0);
CGRect rect;
rect.origin = point;
rect.size = CGSizeMake (1.0, 1.0);
UIMenuController *menu = [UIMenuController sharedMenuController];
menu.menuItems = s_menuItems;
[menu setTargetRect: rect inView: self];
[menu setMenuVisible: YES animated: YES];
</pre>
If you have work to do when the menu disappears (such as deselecting a tableview cell), you can listen for <code>UIMenuControllerWillHideMenuNotification</code> or one of its kin.<p>
<li> <b>Binding an NSPopupButton</b> (General->General)<br>
To bind an NSPopupButton:
<ul>
<li> Have an NSArray somewhere that you want to supply the popup
<li> Make an NSArrayController in the nib file (say call it "greeble"), and binds its Value to some keypath (such as your AppDelegate.greebleArray ivar)
<li> Bind the popup's <b>Content</b> to the array controller's arranged objects (greeble.arrangedObjects). These are the objects that are behind the popup.
<li> Bind the popup's <b>Content Values</b> to a key path that includes a displayable string. Assuming the objets in the greebleArray array have a title property, you'd bind it to greeble.arrangedObjects.title
<li> Bind the popup's <b>Selected Object</b> to something, such as an ivar of the type of object that's in the greebleArray. If said ivar is called selectedGreeble, you'd bind to AppDelegate's selectedGreeble.
</ul><p>
<li> <b>Configuring control-shift-D dictionary contents</b> (General->General)<br>
When I go to define a word with control-shift-D, I'm not interested in wikipedia - just give me the definition.
<p>
This system-wide behavior isn't configured in the system preferences, instead in the prefs of the Dictionary App :-| . Go to the preferences, and you get a list of a ton of stuff. Turn off Wikipedia (or turn on / off whatever you want)<p>
<li> <b>Continuous Text Field</b> (General->General)<br>
Courtesy of Jens Bauer. Sometimes you'd like a text field to notify on every text change.
<pre>@interface FSContinousTextField : NSTextField
@end
@implementation FSContinousTextField
- (void) textDidChange: (NSNotification *) aNotification {
[[self target] performSelector:[self action] withObject:self];
}
@end</pre><p>
<li> <b>Convert a preprocessor symbol to an NSString</b> (General->General)<br>
<pre>#define CONVERT_SYMBOL_TO_NSSTRING_2(x) @#x
#define CONVERT_SYMBOL_TO_NSSTRING(x) CONVERT_SYMBOL_TO_NSSTRING_2(x)
NSString *version = CONVERT_SYMBOL_TO_NSSTRING (BUILD_NUMBER);
</pre>
(Thanks to TVL for helping me figure this one out)<p>
<li> <b>Creating a UUID</b> (General->General)<br>
<pre>CFUUIDRef cfuuid = CFUUIDCreate (kCFAllocatorDefault);
NSString *uuid = (NSString *)CFUUIDCreateString (kCFAllocatorDefault, cfuuid);
CFRelease (cfuuid);</pre>
Because a core foundation "Create" function is called, you're responsible for releasing <code>uuid</code> when you're done with it.<p>
<li> <b>Defaults-writing an array</b> (General->General)<br>
<pre>% defaults write com.borkware.snongflozzle ookook -array thing1 thing2 thing3</pre><p>
<li> <b>Discovering when exiting a sub event loop</b> (General->General)<br>
<code>[self performSelector: @selector(whatever) withObject: nil afterDelay: 0];</code>
<p>
The selector gets posted after a sub event loop finishes. You can use this for finding out when a live slider is done being manipulated, for instance<p>
<li> <b>Easy command-line argument handling</b> (General->General)<br>
You can use NSUserDefaults to sniff your command line. So using something like this:
<pre>
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
gridType = [defaults objectForKey: @"gridType"];
</pre>
Will have the value of "flarn" whether you do something like
<pre>% defaults write com.borkware.BorkStitch gridType flarn</pre>
or
<pre>% ./build/Debug/BorkStitch.app/Contents/MacOS/BorkStitch -gridType flarn</pre><p>
<li> <b>Enabling ARC</b> (General->General)<br>
Turn on ARC at the command line by feeding the compiler the <code>-fobjc-arc</code> flag.<p>
<li> <b>Fine-grained warning control</b> (General->General)<br>
You can turn on/off particular warnings. Here is an alignment warning, but in a situation where the code actually does generate an aligned address, but the compiler can't convince itself:
<pre>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-align"
scan = (fileinfo *) (((char *) scan) + scan->length);
#pragma clang diagnostic pop
</pre><p>
<li> <b>Giving the event loop some love</b> (General->General)<br>
Say you're in a callback environment in the main thread, but need to update the UI, like to update a progress meter. You can give the runloop a chance to do one flip by doing:
<pre>void giveSomeLove ()
{
// give the app some love so it'll update the window
[[NSRunLoop currentRunLoop] runUntilDate: [NSDate distantPast]];
} // giveSomeLove</pre><p>
<li> <b>Iterating through an NSIndexSet</b> (General->General)<br>
<pre>
unsigned index;
for (index = [indexSet firstIndex];
index != NSNotFound; index = [indexSet indexGreaterThanIndex: index]) {
...
}
</pre>
(courtesy of mikeash)<p>
<li> <b>Loading a nib file</b> (General->General)<br>
<pre>
if ([NSBundle loadNibNamed: @"theNibName.nib" owner: self]) {
... life is happy
} else {
... couldn't load the nib
}
</pre><p>
<li> <b>Make an NSValue out of an NSRect</b> (General->General)<br>
<pre>
NSRect selectionRect = ...;
NSValue *value;
value = [NSValue valueWithRect: selectionRect];
</pre><p>
<li> <b>Making a moby file out of a hierarchy</b> (General->General)<br>
<pre>
% find . -name "*.h" -print -exec cat {} \; > ~/moby-file.h
</pre><p>
<li> <b>Making naked memory autoreleased</b> (General->General)<br>
(courtesy of Mike Ash, who fixed some errors in the previous version of this quickie)
<pre>void *tempCopyOf(void *data, NSUInteger size)
{
return data ? [[NSData dataWithBytes:data length:size] mutableBytes] : NULL;
}
</pre>
OBTW, these techniques will fail under GC. Here's how you'd write a working one under GC:
<pre>
void *tempCopyOf(void *data, NSUInteger size)
{
void *buffer = NULL;
if( data ) {
buffer = NSAllocateCollectable(size, 0);
memcpy(buffer, data, size);
}
return buffer;
}
</pre>
Unfortunately I'm not aware of a nice way that works in dual-mode code.<p>
<li> <b>Open path sheet in the Finder</b> (General->General)<br>
Open a Finder window, then type command-shift-g. Works in standard file dialogs too.<p>
<li> <b>Quicktime X Player A/V controls</b> (General->General)<br>
When watching pre-recorded sessions from technical conferences, I like to speed them up. Usually I can speed up things to 1.5x to 1.8x speed and still understand what's going on. I was able to speed Matt Gemmell up to 2.5x and could still understand him. Amazing. Quicktime 7 had nice A/V controls to speed up the video. You can download Quicktime 7 and use its player.
<p>
Quicktime X's player doesn't have an A/V controls window, but you can option-click the fast-forward button to increase the speed by 0.1x. Keep clicking to go faster. Unfortunately you can't pause when sped up, so if you pause, you have to re-click to your desired speedup.<p>
<li> <b>Reading a file as an NSData</b> (General->General)<br>
<pre> NSString *filename = @"/this/is/my/file/path";
NSData *data;
data = [NSData dataWithContentsOfFile: filename];
</pre><p>
<li> <b>Resetting user defaults</b> (General->General)<br>
Sometimes you just want to blow away all your app's defaults and return to the factory default settings. <a href="http://rooswitch.com/">RooSwitch</a> is a great way to do it for your app for testing or debugging. Or if you need to do it in code:
<pre>
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[defs removePersistentDomainForName: appDomain];
</pre><p>
<li> <b>Setting iPhone status bar to black</b> (General->General)<br>
In the app's info.plist, set <code>UIStatusBarStyle</code> to <code>UIStatusBarStyleOpaqueBlack</code><p>
<li> <b>Toggling an index in an NSMutableIndexSet</b> (General->General)<br>
<code> NSUInteger index = indexPath.row;
if ([_selection containsIndex: index]) [_selection removeIndex: index];
else [_selection addIndex: index];
</code><p>
<li> <b>Turn off the Lion "autorepeat shows accent chooser"</b> (General->General)<br>
I actually want my characters to autorepeat rather than seeing the accent chooser. Run this in the terminal, and restart affected apps:
<pre>defaults write -g ApplePressAndHoldEnabled -bool false</pre>
(thanks to Paul Kafasis)<p>
<li> <b>Turn off the Lion window animation</b> (General->General)<br>
<pre>defaults write NSGlobalDomain NSAutomaticWindowAnimationsEnabled -bool NO</pre><p>
<li> <b>Turning off Yosemite focus ring highlight animation</b> (General->General)<br>
I'm not a fan of animations in general. I'm really not a fan of the Yosemite focus ring animation. Usually I'm already half-way through typing by the time the animation ends. Here's a way to turn it off:
<pre>
% defaults write -globalDomain NSUseAnimatedFocusRing -bool NO
</pre>
Thanks to <a href="https://twitter.com/rsesek/status/563072586754850816">@rsesek</a>.<p>
<li> <b>Unicode characters in C strings</b> (General->General)<br>
C strings (and Objective-C @"Strings") are ascii by default, so to include unicode characters, you need to escape-U them:
<pre>
printf ("\u3232_\u3232\u2122\n");
</pre><p>
<li> <b>Using NSInvocation</b> (General->General)<br>
When you have a target and a selector, it's pretty easy to invoke the selector on the target if you're passing one or two parameters, with performSelector:, performSelector:withObject:, and so on. If you need to pass three arguments, or things that aren't objects, then you need to use NSInvocation.
<p>
In this case, the selector being called takes two arguments, one of which is an object, the other is an <code>NSTimeInterval</code>. The <code>atIndex:</code> jazz starts with 2 so that the self parameter and the selector can be passed to the method.
<pre> NSMethodSignature *signature = [target_ methodSignatureForSelector:selector_];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector_];
[invocation setTarget:target_];
[invocation setArgument:&self
atIndex:2];
[invocation setArgument:&lastWait_
atIndex:3];
[invocation invoke];</pre><p>
<li> <b>Using an NSOpenPanel</b> (General->General)<br>
Using an NSOpenPanel with blocks:
<pre>- (IBAction) openEarthinizerDoc: (id) sender {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel beginSheetModalForWindow: self.window
completionHandler: ^(NSInteger result) {
if (result == NSFileHandlingPanelOKButton) {
NSURL *url = [[panel URLs] objectAtIndex: 0];
[self openURL: url];
}
}];
} // openEarthinizerDoc
</pre><p>
<li> <b>Using an NSOpenPanel</b> (General->General)<br>
<pre> NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setPrompt: @"Stuff for the Choose Button"];
[panel beginSheetForDirectory: nil
file: nil
types: [NSArray arrayWithObject: @"buf"] // or other file types
modalForWindow: window
modalDelegate: self
didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
contextInfo: nil];</pre>
and the didEndSelector implementation looks kinda like this:
<pre>- (void) openPanelDidEnd: (NSOpenPanel *) sheet
returnCode: (int) returnCode
contextInfo: (void *) context
{
if (returnCode == NSOKButton) {
NSArray *fileNames = [sheet filenames];
NSLog (@"wooOOooot! %@", [fileNames objectAtIndex: 0]);
}
} // openPanelDidEnd</pre><p>
<li> <b>Verifying Localizable.strings</b> (General->General)<br>
if there's an error in <code>Localizable.string</code>, your <code>NSLocalizedString()</code> call will not look up the strings in the file. To verify the file, do something like this:<p>
<code>% pl < Localizable.strings</code></code><p>
<li> <b>show predefined preprocessor macros</b> (General->General)<br>
<code>% gcc -E -dM - </dev/null</code><p>
<li> <b></b> (General->Hacks)<br>
Getting a really annoying warning with nothing actionable when implemented secure (de)coding?
<pre>
*** -[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSString' (0x1ec071f50)
[/Library/Developer/CoreSimulator/Volumes/iOS_22B81/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS
18.1.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework]' for key '',
even though it was not explicitly included in the client allowed classes set:
'<decode: bad range for [%{public}@] got [offs:647 len:474 within:0]>'.
This will be disallowed in the future.<decode: bad range for [%{public}@]
got [offs:1121 len:1 within:0]>
</pre>
you can set this breakpoint to break there. (wouldn't it have been nice if they had mentioned that in the warning?):
<pre>
-[NSCoder _warnAboutPlistType:forKey:missingInAllowedClasses:]
</pre><p>
<li> <b>A Quieter NSLog</b> (General->Hacks)<br>
<pre>
// NSLog() writes out entirely too much stuff. Most of the time I'm
// not interested in the program name, process ID, and current time
// down to the subsecond level.
// This takes an NSString with printf-style format, and outputs it.
// regular old printf can't be used instead because it doesn't
// support the '%@' format option.
void QuietLog (NSString *format, ...) {
va_list argList;
va_start (argList, format);
NSString *message = [[[NSString alloc] initWithFormat: format
arguments: argList] autorelease];
fprintf (stderr, "%s\n", [message UTF8String]);
va_end (argList);
} // QuietLog
</pre><p>
<li> <b>Having powerbook not wake from sleep when the lid opens</b> (General->Hacks)<br>
<code># pmset lidwake 0</code><p>
<li> <b>Converting from a path to an FSRef</b> (General->Random)<br>
<pre> NSString *pathString = ... whatever ...;
FSRef ref;
status = FSPathMakeRef ((const UInt8 *)[pathString fileSystemRepresentation],
&ref, NULL);
if (status != noErr) {
NSLog (@"bummer. couldn't make FSREf from path '%@'",
pathString);
}</pre><p>
<li> <b>Derezzing resource files</b> (General->Random)<br>
I needed to derez a resource file, with the data living in the resource fork, and get editable STR and STR# entities. Here's one way to do it, with the moving pieces in bold.
<pre>% /Developer/Tools/DeRez -useDF <b>./blarg.rsrc</b> /Developer/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon/MacTypes.r > <b>oopack2</b></pre>
And the reverse process is like:
<pre>% /Developer/Tools/Rez -o <b>blah.rsrc</b> -useDF /Developer/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon/MacTypes.r <b>oopack2</b></pre><p>
<li> <b>Making a Cocoa moby file</b> (General->Random)<br>
<pre>% cat /System/Library/Frameworks/Foundation.framework/Headers/*.h > ~/moby
% cat /System/Library/Frameworks/AppKit.framework/Headers/*.h >> ~/moby
% chmod 444 ~/moby</pre><p>
<li> <b>Varargs in Objective-C</b> (General->Random)<br>
This processes a nil-terminated sequence of strings:
<pre>- (void) snorgWaffle: (NSString *) start, ... {
va_list argList;
va_start (argList, start);
NSString *string = start;
while (string != nil) {
NSLog (@"Done did saw string '%@'", string);
string = va_arg (argList, NSString *);
}
va_end (argList);
} // snorgWaffle</pre>
and can be called like
<pre>[self snorgWaffle:@"red", @"planet", @"zeitgeist", nil];</pre><p>
<li> <b>printf'ing / NSLogging strings without a terminating zero byte</b> (General->Random)<br>
<pre> printf ("hello %.*s\n", 5, "there is no good going on");
NSLog (@"hello %.*s\n", 5, "there is no good going on");</pre>
results in
<pre>hello there
2007-05-04 09:14:15.575 printf[4914] hello there</pre>
<p>
Similarly, something like <code>printf ("hello %*.*s\n", 10, 5, "florgsnorgle");</code> will right-justify 'florg' in a field of 10 characters.
<p>
(Thanks to TVL for this goodie)<p>
<li> <b>Accessing a class method from an instance.</b> (General->Swift)<br>
Say you have an instance, and you want to call a class method, similar to objc where you could do something like <tt>self.class.insets.left</tt>. Use <tt>type(of: self).methodName()</tt>
<pre>
class Bargle {
static func greeble() {
print("Blargle greeble")
}
func oopack() {
type(of: self).greeble()
}
}
let bargle = Bargle()
bargle.oopack()
</pre>
This will cause <i>Blargle greeble</i><p>
<li> <b>Making a Data out of an array of UInt8</b> (General->Swift)<br>
<pre>
var payload: [UInt8] = [0x09, 0x00, 0x07, 0x00, 0x12, 0x00, 0x64, 0x02, 0x03, 0x04,
0x09, 0x81, 0x12, 0x00, 0x07, 0x00, 0x32, 0x04, 0x05, 0x06,
0x00, 0x00, 0x00, 0x00]
let bufferPointer = UnsafeBufferPointer(start: &payload, count: payload.count)
let buffer = Data(buffer: bufferPointer)
</pre><p>
<li> <b>sizeof in swift</b> (General->Swift)<br>
Say you have a struct:
<pre>
struct Thingie {
let blah: Uint8
let greeble: UInt8
let splunge: UInt8
}
</pre>
Get the <tt>sizeof</tt> via
<pre>
let packetLength = MemoryLayout<Thingie>.size
</pre>
That's the packed size. To account for alignment/padding, use <tt>stride</tt>.<p>
<li> <b>Am I Being Debugged?</b> (Debugging->General)<br>
<pre>int AmIBeingDebugged(void)
{
int mib[4];
struct kinfo_proc info;
size_t size;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
size = sizeof(info);
info.kp_proc.p_flag = 0;
sysctl(mib,4,&info,&size,NULL,0);
return ((info.kp_proc.p_flag & P_TRACED) == P_TRACED);
}
#define StopIfInDebugger() __asm__ volatile ("twnei %0,0" : : "r" (AmIBeingDebugged()))</pre>
(courtesy of arwyn on #macdev. Useful uses: controlling voluminous console output when running in gdb, or if running non-debugged, an assert routine that generates stack trace and logs it when not-being debugged, but just traps into the debugger at the point of failure when being debugged)<p>
<li> <b>Enabling core files</b> (Debugging->General)<br>
Core files are handy to have around if your program crashes with a SEGV, bus error, or if it raises an abort(). You can dig into the core file with <code>gdb</code> There are two ways to enable core files:
<ul>
<li> in your C shell do<br>
<code>% limit coredumpsize unlimited</code><br>
(you can put this into your <code>.cshrc</code>. I don't know what to do for <code>bash</code>)<p>
<li> Or in your program do<br>
<code>struct rlimit corelimit = { RLIM_INFINITY, RLIM_INFINITY };<br>
setrlimit (RLIMIT_CORE, &corelimit);</code>
You may need to also add<br>
<code>#include <sys/types.h><br>
#include <sys/time.h><br>
#include <sys/resource.h></code>
</ul>
<p>
Core files seem to be getting dumped into <code>/cores</code>, rather than the directory the program was run from.<p>
<li> <b>NSDebug.h</b> (Debugging->General)<br>
Take a look at <code>/System/Library/Frameworks/Foundation.framework/Headers/NSDebug.h</code>. Lots of low-level unsupported APIs for mucking around with gory technical details.<p>
<li> <b>Triggering a low-memory warning on the device</b> (Debugging->General)<br>
You can trigger a low-memory warning in the simulator, but sometimes you use one of the myriad of APIs that aren't supported in the simulator, but you still need to track down something ultimately triggered by a low-memory warning.
<p>
Put this somewhere convenient to trigger - in a timer, under a button, or something. Obviously, you don't want to ship with it.
<pre>[[UIApplication sharedApplication] performSelector:@selector(_receivedMemoryNotification)];</pre><p>
<li> <b>Turning off Panther's "Program quit - send a report to Apple" dialog</b> (Debugging->General)<br>
During development, it's annoying to get the "Your program just crashed! Send a report to Apple?". You can turn that off by entering this in the terminal:<p>
<code>% defaults write com.apple.CrashReporter DialogType none</code>
<p>
Turn turn it back on, replace <code>none</code> with <code>prompt</code> to get the original behavior. See <a href="http://developer.apple.com/qa/qa2001/qa1288.html">for details</a>.<p>
<li> <b>Breaking on "Deallocation of a pointer not malloced" messages</b> (Debugging->gdb)<br>
Put a breakpoint on <code>fprintf</code><p>
<li> <b>Eating Brains</b> (Debugging->gdb)<br>
You can turn on Zombies to catch uses of objects after they have been released. Here's an easy way to do it in the gdb console:
<pre>
% gdb build/Debug/Snorkfizzle.app/Contents/MacOS/Snorkfizzle
(gdb) set env NSZombieEnabled=YES
(gdb) fb -[_NSZombie methodSignatureForSelector:]
(gdb) run
</pre>
Then exercise your app and trip the error.<p>
<li> <b>Get a stack trace of all threads</b> (Debugging->gdb)<br>
<code>(gdb) thread apply all where</code><p>
<li> <b>Setting a breakpoint before shared libraries are loaded</b> (Debugging->gdb)<br>
Need to set a breakpoint, say on a Cocoa library function, or something in a shared library you're loading later? Set a "<b>f</b>uture <b>b</b>reakpoint":<br>
<code>(gdb) fb -[NSTextView drawRect:]</code><p>
<li> <b>Tracing message sends using gdb</b> (Debugging->gdb)<br>
<pre>
b objc_msgSend
comm
silent
printf "%c[%s %s]\n", $r3&&((id)$r3)->isa->info&2?'+':'-', $r3?((id)$r3)->isa->name:"nil", $r4
cont
end
b objc_msgSend_rtp
comm
silent
printf "%c[%s %s]\n", $r3&&((id)$r3)->isa->info&2?'+':'-', $r3?((id)$r3)->isa->name:"nil", $r4
cont
end
</pre>
And you'll get some output like this:
<pre>
-[NSTableColumn _bindingAdaptor]
+[NSBinder binderClassesForObject:]
+[NSBinder _allBinderClasses]
+[NSDisplayPatternTitleBinder isUsableWithObject:]
+[NSBox self]
</pre>
(courtesy of <a href="http://www.livejournal.com/users/mayoff/6890.html">Rob Mayoff</a>)<p>
<li> <b>Break on szone_error not working</b> (gdb->General)<br>
Sometimes when you have memory corruption issues, the malloc library happily informs you:
<pre>Borkdoku(11062,0xcec0600) malloc: *** error for object 0xd109010:
incorrect checksum for freed object - object was probably modified
after being freed, break at szone_error to debug</pre>
Which is fine and dandy, but it lies. I've never gotten <code>szone_error</code> to actually do anything. Try breaking on <b><code>malloc_printf</code></b> instead.<p>
<li> <b>Breaking on exceptions</b> (gdb->General)<br>
It can be annoying tracking down the cause of thrown exceptions in Cocoa. you get a notice like
<code>2007-05-05 17:18:00.702 QueenStitcher[2804:117] *** Assertion failure in -[NSColorWell setColor:], NSColorWell.m:497, u suk l0s3r</code>, and then the runloop happily runs again, giving you no clue where the problem is. I tell <code>gdb</code> to always break on Cocoa exceptions:
<pre>fb -[NSException raise]
fb objc_exception_throw()</pre>
For maximal enjoyment, add these two lines to your <code>~/.gdbinit</code> file, so they'll get set no matter how you invoke <code>gdb</code> (no need to add these to every single project, for instance).
<p>
I've been told VoiceOver uses exceptions heavily, so if you're doing VoiceOver development, these breaks may cause you pain.<p>
<li> <b>Displaying four-character ints</b> (gdb->General)<br>
Old-school Mac programming (and Quicktime, and other places) use four-character ints, things like <code>'bork'</code>. You can have gdb print them out if you need to look at one or two of them:
<pre>
(gdb) print/T 1936746868
$4 = 'spit'
</pre>
(thanks to Daniel Jalkut for the print/T trick)<p>
<li> <b>Finding 'self' on Intel</b> (gdb->General)<br>
<code>(gdb) po *(int*)($ebp+8)</code><p>
<li> <b>Hexdump in gdb</b> (gdb->General)<br>
<pre>(gdb) x /80xb attrBuffer</pre>
will give you something like
<pre>0x7fff5fbfeeb0: 0x14 0x00 0x00 0x00 0xdb 0x0b 0x19 0x4c
0x7fff5fbfeeb8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fff5fbfeec0: 0x00 0x00 0x00 0x00 0xec 0xc8 0x62 0xe5
0x7fff5fbfeec8: 0xc7 0x48 0x18 0xd2 0xe7 0x9e 0x88 0xd8
0x7fff5fbfeed0: 0xd9 0x60 0x9f 0x61 0xea 0x37 0x3b 0x0c
0x7fff5fbfeed8: 0x87 0x1b 0x4e 0x7e 0x6a 0x26 0x98 0x62
0x7fff5fbfeee0: 0x6d 0x01 0xed 0xed 0x17 0x6d 0xae 0x05
0x7fff5fbfeee8: 0xe7 0x22 0xd0 0x96 0x33 0x34 0x16 0x1a
0x7fff5fbfeef0: 0x17 0xa7 0x87 0xb2 0x76 0x7b 0x29 0x0b
0x7fff5fbfeef8: 0xea 0x51 0x4c 0x45 0x4f 0x45 0x65 0x88</pre><p>
<li> <b>Ignoring signals</b> (gdb->General)<br>
<code>(gdb) handle SIGTRAP nostop</code>
<p>
The signal still goes to your program. Another handy option is 'ignore' to prevent it coming to the program. Also there is 'print' to print a message go on.<p>
<li> <b>Lie in wait for a launching process</b> (gdb->General)<br>
Attaching to a running process is pretty nifty, handy for debugging daemons started by launchd. But you miss all the fun stuff that happens at program launch before you you can attach to it.
<p>
The <code>attach</code> command's <code>-waitfor</code> option to make gdb lie in wait for a launching process:
<pre>
% gdb /some/app/blah
(gdb) attach -waitfor blah
</pre>
Now when a <code>blah</code> process launches, gdb will stop it and attach to it.<p>
<li> <b>Printing method arguments</b> (gdb->General)<br>
If you've hit a breakpoint on a method that doesn't have debug symbols, you can sometimes get useful information by looking in the processor registers. Arguments start in <code>$r3</code> and go up from there. For Objective-C method sends, <code>$r3</code> has 'self', and <code>$r4</code> has the name of the method. Subsequent arguments are in <code>$5</code> and so on.
<pre>
(gdb) print (char*) $r4
$5 = 0x90874160 "drawRect:"
(gdb) po $r5
<BWStitchView: 0x1a6670>
</pre><p>
<li> <b>Printing object retain count in gdb</b> (gdb->General)<br>
In the gdb console:<br>
<code>(gdb) print (int)[theObject retainCount]</code>
<p>
If you're expecting to have an excessively high number of retains, you can use <code>(unsigned int)</code> in the cast. I find <code>(int)</code> a skootch faster to type.<p>
<li> <b>Printing wide character strings</b> (gdb->General)<br>
gdb won't by default let you print wide character strings. <a href="http://nic-nac-project.de/~skypher/wchar.gdb">Here is a little bit of gdb code that'll let you print them</a>. In case that page moves, here is the relevant stuff. Paste this into your <code>.gdbinit</code> and then you can use <code>wchar_print</code>:<pre>
define wchar_print
echo "
set $i = 0
while (1 == 1)
set $c = (char)(($arg0)[$i++])
if ($c == '\0')
loop_break
end
printf "%c", $c
end
echo "
end
document wchar_print
wchar_print <wstr>
Print ASCII part of <wstr>, which is a wide character string of type wchar_t*.
end
</pre><p>
<li> <b>Seeing functions and selectors</b> (gdb->General)<br>
<code>info selectors</code> will show you all of the selectors in the application's symbol table. <code>info functions</code> will show you all of the functions. You can supply regular expressions to limit the output.<p>
<li> <b>Using libgmalloc in gdb</b> (gdb->General)<br>
<code>libgmalloc</code> puts guard pages at the end of malloc'd blocks of memory, letting you catch buffer overruns. (This will hugely inflate your program's working set, and may lead to swapping) To turn on <code>libgmalloc</code> in gdb, do this:
<pre>(gdb) set env DYLD_INSERT_LIBRARIES /usr/lib/libgmalloc.dylib</pre><p>
<li> <b>calling objective-C methods in gdb</b> (gdb->General)<br>
To call an Objective-C method in the gdb console, you have to cast the return type (since gdb doesn't really know what the return value is):
<pre>
(gdb) call <b>(void)</b>[textField setStringValue: @"Bork"]
</pre><p>
<li> <b>gdb'ing specific architectures</b> (gdb->Debugging)<br>
With a fat binary (32/64bit), gdb picks the 64 bit version. If you're trying to debug a 32-bit unit test on the command-line though, the 64-bitness of /Developer/Tools/otest gets in the way:
<pre><b>% gdb /Developer/Tools/otest</b>
2008-10-31 19:29:50.834 otest[711:813] Error loading
/blah/blah/blah/build/Debug/Tests.octest/Contents/MacOS/Tests:
dlopen(/blah/blah/blah/build/Debug/Tests.octest/Contents/MacOS/Tests,
265): no suitable image found. Did find:
/blah/blah/blah/build/Debug/Tests.octest/Contents/MacOS/Tests:
mach-o, but wrong architecture
2008-10-31 19:29:50.887 otest[711:813] The test bundle at
build/Debug/Tests.octest could not be loaded because it is built for a
different architecture than the currently-running test rig (which is
running as unknown).
2008-10-31 19:29:50.904 otest[714:203] *** NSTask: Task create for
path '/blah/blah/blah/build/Debug/Tests.octest/Contents/MacOS/Tests'
failed: 8, "Exec format error". Terminating temporary process.</pre>
You can supply a -arch flag to pick what you want:
<pre><b>% gdb -arch i386 /Developer/Tools/otest</b></pre>
And then debug your 32-bit unit test.<p>
<li> <b>Loading a bundle into a running program</b> (gdb->Hacks)<br>
Sometimes it's handy to load a bundle into a running app to do some poseAsClass: for doing some debugging or reverse engineering. Make a Cocoa bundle which has the code you want to load, then do:
<pre>(gdb) call (id) objc_getClass("NSBundle")
$1 = (struct objc_object *) 0xa0a051d8
gdb) call (id)[$1 bundleWithPath:@"/blah/blah/PoseAsClassBundle.bundle"]
$2 = (struct objc_object *) 0x51467e0
(gdb) call (BOOL)[$2 load]
Reading symbols for shared libraries . done</pre><p>
<li> <b>Disabling control-Z from backgrounding emacs</b> (emacs->General)<br>
I find emacs' control-Z behavior to be pretty annoying (it backgrounds the program if you're in a shell, or hides the window if you're in X). Add this to your <code>.emacs</code> file:<br>
<code>(global-set-key "C-Z" nil)</code><p>
<li> <b>Emacs registers</b> (emacs->General)<br>
Emacs has a number of "registers", which you can treat as directly-addressable clipboards.
<p>
Copy into register 1:
<ol>
<li> select range
<li> M-x copy-to-register
<li> 1
</ol>
<p>
Paste from register 1:
<ol>
<li> M-x insert-register
<li> 1
</ol>
<p>
View the contents of a register
<ol>
<li> M-x view-register
<li> 1
</ol>
<p>
Register names can be single letters, numbers, characters. Upper and lower case are distinct.<p>
<li> <b>Fixing "no job control in this shell"</b> (emacs->General)<br>
Emacs in Mac OS X 10.1.3 (and other versions) has an
annoying habit of having broken shells when you do M-x
shell. You get an error like "Inappropriate ioctl for device, no job control in this shell",
which makes interrupting or backgrounding programs in
shell mode impossible. Domo-kun gave me a one-line
patch to the emacs source:<br>
<code> #define DONT_REOPEN_PTY</code><br>
. Add that to darwin.h and build emacs. You can get the
emacs source from the <a href="http://www.opensource.apple.com/projects/darwin/1.4/projects.html">Darwin projects page</a>. If you'd like a binary, drop <a href="mailto:gimmemacs@borkware.com">us some mail.</a><p>
<li> <b>Fixing emacs C mode indenting</b> (emacs->General)<br>
Here's a way to change the C indenting style to a major style, and override some of the pre-set values (like how emacs 21 changed the bsd indent level from 4 to 8.
Gee thanks guys):<pre>(setq c-default-style "bsd"
c-basic-offset 4)</pre><p>
<li> <b>Fixing emacs backspace in screen</b> (emacs->General)<br>
When running emacs insde of screen, screen helpfully turns the backspace/delete key into "^[[3~", which gets turned into a forward-delete. Unfortunately, just bashing <code>deletechar</code> into <code>backward-delete-char-untabify</code> causes backspace in incremental search to cancel the search, which is annoying.
<p>
One option is to set the TERM env var to rxvt:
<pre>% setenv TERM rxvt</pre>
Before cranking up screen.<p>
<li> <b>Macro recording</b> (emacs->General)<br>
<code>C-x (</code> : start recording keyboard macro<br>
<code>C-x )</code> : stop recording keyboard macro<br>
<code>C-x e</code> : replay current keyboard macro<p>
<li> <b>Make emacs indent code with spaces instead of tabs</b> (emacs->General)<br>
Personally, I prefer emacs' default indentation with a mixture of tabs and spaces. If you're working on a project or for a client that requires indentation with spaces, add this to your <code>.emacs</code> file. This will make spaces the indent character, and use 4 spaces per indent level, for C, C++, and Objective C:<p>
<pre>(setq c-mode-hook
(function (lambda ()
(setq indent-tabs-mode nil)
(setq c-indent-level 4))))
(setq objc-mode-hook
(function (lambda ()
(setq indent-tabs-mode nil)
(setq c-indent-level 4))))
(setq c++-mode-hook
(function (lambda ()
(setq indent-tabs-mode nil)
(setq c-indent-level 4))))
</pre><p>
<li> <b>Making .h files open in objc- mode</b> (emacs->General)<br>
Add to <code>.emacs</code:
<pre>(add-to-list 'auto-mode-alist '("\\.h$" . objc-mode))</pre><p>
<li> <b>Rectangular cut and paste</b> (emacs->General)<br>
<ol>
<li> Move to top-left of source rectangle. <code>C-SPC</code> to set the mark
<li> Move to bottom-right of source rectangle
<li> <code>M-x kill-rectangle</code>
<lI> Move to paste destination
<li> <code>M-x yank-rectangle</code>
</ol><p>
<li> <b>Resetting shell mode's idea of the current working directory</b> (emacs->General)<br>
Sometimes the shell mode will get confused as to what the current working directory is (like if you use aliases to move to a new directory, or if you
use the conveniences like <code>!$</code>). <code>M-x dirs</code> will tell the shell buffer to figure out what the current working directory is.<p>
<li> <b>Restrict editing to the region</b> (emacs->General)<br>
<code>M-x narrow-to-region</code>
<p>
Hides everything not in the current region.<p>
<li> <b>Revisiting / reloading a file in emacs</b> (emacs->General)<br>
The <code>$Id: $</code> tags for CVS are nice, but it can be a pain when you're doing lots of checkins and have to re-load the file each time. You can either execute <code>M-x revert-bufer</code> or bind that to a key, or else use a trick by doing <code>C-x C-v</code> which invokes <code>find-alternate-file</code>, but just so happens to have the current buffer name, so you just have to do <code>C-x C-v RET</code><p>
<li> <b>Running shell command pasting result back into the buffer</b> (emacs->General)<br>
So to run <code>uuidgen</code>, for instance:
<p>
<code>C-U M-!</code> ret <code>uuidgen</code> ret
<p><p>
<li> <b>Scroll line with cursor to the top of the window</b> (emacs->General)<br>
<code>C-U 0 C-L</code><p>
(you can put in another number besides zero to scroll the line with the cursor to that particular line in the buffer)<p>
<li> <b>Setting variables when loading a file</b> (emacs->General)<br>
So say you're working on a project with two-space indents, but most of your other work happens with four-space indents. If the two-space crowd is amenable, add this to the bottom of the file:
<pre>/* For the emacs weenies in the crowd.
Local Variables:
c-basic-offset: 2
End:
*/</pre><p>
<li> <b>Showing current column position</b> (emacs->General)<br>
<code>M-x column-number-mode</code><p>
<li> <b>Sort case-insensitive</b> (emacs->General)<br>
<code>M-x sort-lines</code> uses a case-sensitive search. When that sucks, you can tell emacs to be case insensitive (globally) by doing <code>M-x set-variable RET sort-fold-case RET t</code><p>
<li> <b>Toggling read-only mode in a buffer</b> (emacs->General)<br>
<code>C-X C-Q</code><p>
<li> <b>Turning off command highlighting in shell mode</b> (emacs->General)<br>
Emacs 21, which comes with Mac OS X 10.2, "helpfully" puts into bold the commands you execute in the shell. This drives me nuts, so I figured out how to turn it off. Add this to your .emacs file: <p>
<code>(setq comint-highlight-input nil)</code><p>
<li> <b>Turning off font-lock mode everywhere</b> (emacs->General)<br>
<code>(global-font-lock-mode -1)</code><p>
<li> <b>Turning off incremental-search highlighting</b> (emacs->General)<br>
Emacs 21, which comes with Mac OS X 10.2, has highlighting enabled when doing incremental search (which drives me nuts). You can turn that off by setting this in your .emacs file:<p>
<code>(setq search-highlight nil)</code>
<p>
You may also need to <br>
<code>(setq isearch-lazy-highlight nil)</code><p>
To turn off underlining of matching results. Only some OS X installs need this setting.<p>
<li> <b>Turning off scroll-to-end in shell-mode</b> (emacs->General)<br>
<code>(setq comint-scroll-show-maximum-output nil)</code><p>
<li> <b>Undo within a given region</b> (emacs->General)<br>
<code>C-U C-_</code><p>
<li> <b>Unnarrowing the region</b> (emacs->General)<br>
<code>M-x widen</code><p>
<li> <b>Use only spaces when indenting code</b> (emacs->General)<br>
(setq indent-tabs-mode nil)<p>
<li> <b>Using carriage returns in query-replace / replace-string</b> (emacs->General)<br>
Use <code>C-Q C-J</code> (control-Q control-J) each time you want to include a carriage return. e.g. to double-space everything<p>
<code>M-x replace-string RET C-Q C-J RET C-Q C-J C-Q C-J RET</code>
<p>
Or to put "bloogie " at the beginning of every line<p>
<code>M-x replace-string RET C-Q C-J RET C-Q C-J b l o o g i e SPACE RET</code><p>
<li> <b>compiling emacs .el files</b> (emacs->General)<br>
Big emacs <code>.el</code> files take a long time to load.
You can compile them into <code>.elc</code> files by using:<br>
<code>% emacs -batch -f batch-byte-compile filename.el</code><p>
<li> <b>emacs registers</b> (emacs->General)<br>
Stick something into a register:
<pre>
(select stuff)
C-x r x 1
</pre>
where "1" is the register identifier.
<p>
Getting stuff out of a register:
<pre>
C-x r g 1
</pre><p>
<li> <b>move to previous buffer C-x p</b> (emacs->Hacks)<br>
<pre>
(global-set-key (kbd "C-x p") 'move-to-previous-window)
(defun move-to-previous-window ()
"Move to the previous window."
(interactive)
(other-window -1))
</pre><p>
<li> <b>Balance a tag in SGML mode</b> (emacs->Random)<br>
<pre>C-/</pre><p>
<li> <b>Doing Sheets</b> (NSWindow->General)<br>
To show a sheet in a window, use NSApplication to kick it off:
<pre> [NSApp beginSheet: saveSheet
modalForWindow: window
modalDelegate: self
didEndSelector: @selector(saveSheetDidEnd:returnCode:contextInfo:)
contextInfo: NULL];</pre>
In the controls in the sheet, use something like
<pre> [NSApp endSheet: saveSheet returnCode: NSOKButton];</pre>
To invoke the <code>didEndSelector</code>. Inside of that method, you can check the return code and decide what to do:
<pre>- (void) saveSheetDidEnd: (NSWindow *) sheet
returnCode: (int) returnCode
contextInfo: (void *) contextInfo
{
if (returnCode == NSOKButton) {
// ...
} else if (returnCode == NSCancelButton) {
// ...
} else {
// ...
}
[sheet close];
} // saveSheetDidEnd</pre><p>
<li> <b>Finding the height of the title bar</b> (NSWindow->General)<br>
When you <code>setFrame:</code> for a window, you have to account for the height of the title bar. In the Classic days it was 16 pixels. In Aqua-land, it's currently 22 pixels. But that's not safe to use, so try this instead:
<pre>- (float) titleBarHeight
{
NSRect frame = NSMakeRect (0, 0, 100, 100);
NSRect contentRect;
contentRect = [NSWindow contentRectForFrameRect: frame
styleMask: NSTitledWindowMask];
return (frame.size.height - contentRect.size.height);
} // titleBarHeight</pre>
Rainer Brockerhoff points out that this might miss any system-suppled window decorations at the bottom of the window. There aren't any now, but Mac OS X 10.37 Sabertooth might, so you may want to take into account the y positions of the original rectangle and the newly calculated content rect.<p>
<li> <b>Force an NSWindowController's nib to be loaded</b> (NSWindow->General)<br>
The window controller nib doesn't get loaded until the window is manipulated. This can cause confusion if you do any kind of setup before the window is shown. If you call the <code>window</code> method, that will force the nib file to be loaded. Something like this:
<pre>+ (BWInspector *) sharedInspector
{
static BWInspector *s_inspector;
if (s_inspector == nil) {
s_inspector = [[BWInspector alloc]
initWithWindowNibName: @"BWInspector"];
assert (s_inspector != nil);
// force loading of nib
<b>(void) [s_inspector window];</b>
}
return (s_inspector);
} // sharedInspector</pre><p>
<li> <b>Help! My Sheet won't go away</b> (NSWindow->General)<br>
You may need to call <code>[sheet close]</code> in addition to your <code>[NSApp endSheet: sheet returnCode:23]</code><p>
<li> <b>Making a floating palette</b> (NSWindow->General)<br>
To make a floating palette with small controls, make the palette an NSPanel, and n IB make it a "Utility Window".<p>
<li> <b>Making a panel not eat the key</b> (NSWindow->General)<br>
The default settings for an NSPanel in IB cause the panel to take the main window out of the responder chain, and so commands (like undo) don't propagate to the window the inspector is editing. In IB, make sure the settings "Utility window" and "Non Activating Panel" are selected.<p>
<li> <b>Preventing NSPanels from hiding on program deactivation</b> (NSWindow->General)<br>
Sometimes you want an NSPanel for the nice skinny title bar, but don't want it to hide when another program is made active. Somewhere convenient (like in the awakeFromNib), do something like<p>
<code>[panel setHidesOnDeactivate: NO];</code><p>
<li> <b>Resizing a window with animation</b> (NSWindow->General)<br>
Use <code>setFrame:display:animate:</code> to resize a window, and have the window animate between the two sizes. Remember that Cocoa uses a bottom-left origin, which is a pain when dealing with windows. You want the window's top left to be the same between the old and new sizes, so you have to dink with the origin as well as the size:
<pre>
float delta = ... how much to make the window bigger or smaller ...;
NSRect frame = [window frame];
frame.origin.y -= delta;
frame.size.height += delta;
[window setFrame: frame
display: YES
animate: YES];
</pre><p>
<li> <b>Resizing a window with animation and cool cross-fade effect with views</b> (NSWindow->General)<br>
<code>NSViewAnimation</code> lets you resize a window and cross-fade some views in one operation. (note I've had <a href="/miniblog/one-entry?entry%5fid=49737">problems</a> with the window size getting a couple of pixels off using this. Hopefully either I'm doing something dumb, or Apple fixes a bug). This code resizes the window, and changes the view that lives inside of a box. This is like how the Pages inspector works (except Pages doesn't do the gratuitous animation effect)
<pre> NSRect newWindowFrame = ... the new window size;
NSDictionary *windowResize;
windowResize = [NSDictionary dictionaryWithObjectsAndKeys:
window, NSViewAnimationTargetKey,
[NSValue valueWithRect: newWindowFrame],
NSViewAnimationEndFrameKey,
nil];
NSDictionary *oldFadeOut = nil;
if (oldView != nil) {
oldFadeOut = [NSDictionary dictionaryWithObjectsAndKeys:
oldView, NSViewAnimationTargetKey,
NSViewAnimationFadeOutEffect,
NSViewAnimationEffectKey, nil];
}
NSDictionary *newFadeIn;
newFadeIn = [NSDictionary dictionaryWithObjectsAndKeys:
newView, NSViewAnimationTargetKey,
NSViewAnimationFadeInEffect,
NSViewAnimationEffectKey, nil];
NSArray *animations;
animations = [NSArray arrayWithObjects:
windowResize, newFadeIn, oldFadeOut, nil];
NSViewAnimation *animation;
animation = [[NSViewAnimation alloc]
initWithViewAnimations: animations];
[animation setAnimationBlockingMode: NSAnimationBlocking];
[animation setDuration: 0.5]; // or however long you want it for
[animation startAnimation]; // because it's blocking, once it returns, we're done
[animation release];</pre><p>
<li> <b>Running a window modally</b> (NSWindow->General)<br>
<pre> // I am the very modal of a modern major general.
int blah = [NSApp runModalForWindow:[self window]];
</pre>
and then elsewhere, like in your OK or cancel button
<pre> [NSApp stopModalWithCode:NSOKButton];</pre><p>
<li> <b>Using an NSWIndowController to load a window</b> (NSWindow->General)<br>
Here's how I like doing things. First make a subclass of <code>NSWindowController</code>:
<pre>@interface BWInspector : NSWindowController
{
// ivars, IBOutlets, etc
}
// IBActions, etc
+ (BWInspector *) sharedInspector;
@end // BWInspector
</pre>
Then make a nib file with the window, the controls, and other fun stuff you want. This one lives in <code>English.lproj/BWInspector.nib</code>
<p>
Then in that class method load the window:
<pre>+ (BWInspector *) sharedInspector
{
static BWInspector *g_inspector;
if (g_inspector == nil) {
g_inspector = [[BWInspector alloc]
initWithWindowNibName: @"BWInspector"];
assert (g_inspector != nil); // or other error handling
[g_inspector showWindow: self];
}
return (g_inspector);
} // sharedInspector</pre>
That will load the nib file, set up the connections and then open the window for display<p>
<li> <b>I want my window's background to be yellow</b> (NSWindow->Hacks)<br>
Why you'd want to do that, I don't know, but someone asked me how to turn the window background yellow. Since the window has a contentView NSView in it, this adds a category on it that draws the background with a transparent yellow. You also get the pinstripe effects. Just put this code anywhere.<pre>
@implementation NSView (BWYellowView)
- (void) drawRect: (NSRect) rect
{
NSColor *transparentYellow;
rect = [self bounds];
transparentYellow = [NSColor colorWithCalibratedRed: 1.0
green: 1.0
blue: 0.0
alpha: 0.333];
[transparentYellow set];
[NSBezierPath fillRect: rect];
} // rect
@end // BWYellowView
</pre>
I have no idea how you colorize the titlebar.<p>
<li> <b>Forwarding key events to another window</b> (NSWindow->Random)<br>
To forward key events to another window, make subclass of <code>NSWindow</code> and override <code>sendEvent</code>:<pre>
- (void) sendEvent: (NSEvent *) anEvent
{
if ([anEvent type] == NSKeyDown) {
[someOtherWindow sendEvent: anEvent];
} else {
[super sendEvent: anEvent];
}
} // sendEvent
</pre>
It's up to you to figure out what "<code>someOtherWindow</code>" is, whether it's a delegate or an instance variable that holds the other window.<p>
<li> <b>Disabling the "autorepeat pops up accent menu"</b> (Hacks->General)<br>
Remember the good old days when holding down 'e' would give you a line of eeeeeeeeeeeeeeeeee? With 10.7, it now pops up an accent panel. If you already know how to make the accents and want your autorepeat back, run this <code>defaults write</code> and relaunch your apps.
<pre>% defaults write -g ApplePressAndHoldEnabled -bool false</pre><p>
<li> <b>Making naked memory autoreleased</b> (Hacks->General)<br>
From the devious mind of Rainer Brockerhoff : given a pointer + length, dynamically allocate a copy and make it autoreleased, without (directly) involving cocoa objects. One handy usage: giving a plugin a copy of some struct, but be protected against the plugin messing it up.
<pre>static void *tempCopyOf(void *data,UInt32 size) {
void *buffer = NULL;
if (data) {
buffer = malloc(size);
if (buffer) {
bcopy(data,buffer,size);
[NSData dataWithBytesNoCopy: buffer length: size freeWhenDone: YES];
}
}
return (buffer);
}</pre>
You can get really fancy and assign <code>buffer</code> to <code>calloc(1, size)</code> if you want to give it "pass NULL to get a zero-filled buffer back" semantics.
<p>
Also note this doesn't play well with garbage collection - caveat nerdor. (Thanks to Ken Ferry for that note)<p>
<li> <b>Posing as a class</b> (Hacks->General)<br>
Say you wanted to take a peek at how NSSpellChecker does its thing. You can make a subclass, override methods, log interesting stuff, and then send super. Use <code>poseAsClass:</code> to hook yourself into the matrix during the <code>+load</code> method:
<pre>@interface BorkSpellChecker : NSSpellChecker
{
}
@end // BorkSpellChecker
@implementation BorkSpellChecker
+ (void) load
{
NSLog (@"posing");
[self poseAsClass: [NSSpellChecker class]];
} // load
- (void) ignoreWord: (NSString *) word inSpellDocumentWithTag: (int) tag
{
NSLog (@"ignore word: %@ intag: %d",
word, tag);
[super ignoreWord: word
inSpellDocumentWithTag: tag];
} // ignoreWord</pre><p>
<li> <b>Turning off screen dimming / locking</b> (Hacks->General)<br>
iDevices after a period of no user interaction will dim the screen and eventually lock it. If you have a hands-free app, or one that's primarily passive, this is unuseful behavior.
<p>
The UIApplication <code>itemTimerDisabled</code> controls whether the screen should dim. Disable it by enabling it:
<pre>[[UIApplication sharedApplication] setIdleTimerDisabled: YES];</pre>
or equivalently
<pre>[UIApplication sharedApplication].idleTimerDisabled = YES;</pre>
<p>
Unfortunately, this is not a reliable operation. Sometimes twiddling a UI control or playing music will let the screen dim and lock up. What I ended up doing is toggling the value in a timer:
<pre>[[UIApplication sharedApplication] setIdleTimerDisabled: NO];
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];</pre><p>
<li> <b>Making the VPN survive fast user switching.</b> (Hacks->Hacks)<br>
Testing admin vs non admin user scenarios while connected to the mothership VPN is a pain. Heres a way to modify the system configuration plist to make the VPN connection survive fast user switching.
<p>
Open up Internet Connect, select your VPN icon in the toolbar, and choose options from the Connect menu. Uncheck "Disconnect when switching user accounts".<p>
<li> <b>Exposing documents directory to Files / Finder</b> (Hacks->Random)<br>
Set these both to YES in the Info.plist
<pre>
UIFileSharingEnabled
LSSupportsOpeningDocumentsInPlace
</pre><p>
<li> <b>Stopping a chirping G5</b> (Hacks->Random)<br>
Some (many? all?) G5s make a quiet chirping sound at one-second intervals, like a quiet cricket. It might also chirp when dragging windows or doing something with expose. If that bothers you (like it bothered me), go download CHUD (which you should have already), go to the Processor system preference panel, and turn off "allow nap". That may shorten the life of your machine. But what that means, I have no idea.<p>
<li> <b>Checking for modifier keys</b> (NSView->General)<br>
<p>
In your mouseDown:
<pre>
if ([event modifierFlags] & NSShiftKeyMask) {
constrain = YES;
}
</pre>
If you need to check them outside of your mouseDown, and if you're on 10.6 or beyond, you can use <code>+[NSEvent modifierFlags];</code>
<p>
If you're stuck in 10.5 or older, you need to do something like this:
dip into carbon:
<ul>
<li> GetCurrentEventKeyModifiers returns the modifiers of the last event processed by the event dispatcher. This is usually sufficient.
<li> GetCurrentKeyModifiers returns the hardware state of the modifiers.
</ul>
Carbon and AppKit use different constants for the modifiers, however, so you may need to convert between them depending on your situation. (Thanks to Peter Hosey for the Carbon trick)
<p>
(Thanks to Mike Ash for pointing out the new 10.6 API)<p>
<li> <b>Converting an event to view coordinates</b> (NSView->General)<br>
<code>NSPoint point = [self convertPoint: [event locationInWindow] fromView: nil];</code><p>
<li> <b>Detecting arrow keys</b> (NSView->General)<br>
<pre>
- (void) keyDown: (NSEvent *) event
{
NSString *characters;
characters = [event characters];
unichar character;
character = [characters characterAtIndex: 0];
if (character == NSRightArrowFunctionKey) {
[self moveSelectedBlockBy: 1];
} else if (character == NSLeftArrowFunctionKey) {
[self moveSelectedBlockBy: -1];
} <i> ... and look at whatever other keys you want</i>
}
</pre><p>
<li> <b>Finding a subview based on tag</b> (NSView->General)<br>
Sometimes you're in a situation where you need to get ahold of a view and it's not convenient to set up stuff in IB (like the object needing to get the view isn't in the nib file with the window). You can uses <code>NSView</code>'s <code>viewWithTag:</code> to find it.<p>
<li> <b>Grabbing a view into an image</b> (NSView->General)<br>
In your NSView subclass:
<pre>
[self lockFocus];
NSBitmapImageRep *bits;
bits = [[NSBitmapImageRep alloc]
initWithFocusedViewRect: [self bounds]];
[self unlockFocus];
</pre>
Then you can add it to an <code>NSImage</code> or save it to a file, or whatever.
<p>
If you want to retain the vectoritude of the drawing, you can use <code>[self dataWithPDFInsideRect: [self bounds]]</code>, and then make an <code>NSPDFImageRep</code>. Avoid the <code>NSEPSImageRep</code> since it makes a trip through <code>NSPDFImageRep</code> along with a slow PS to PDF conversion. (Thanks to Peter Hosey for the PDF hint)<p>
<li> <b>Making a custom event tracking runloop</b> (NSView->General)<br>
Sometimes in your mouse tracking code you want to use the <code>NSEventTrackingRunLoopMode</code>, such as you wanting to use an <code>NSNotificationQueue</code> to coalesce updates for your own mouse tracking code. I've been sticking a call to this in my mouseDown: handler for the cases when I want the secondary run loop <pre>
- (void) runEventTrackingRunLoop
{
NSEventType eventType = NSLeftMouseDown;
unsigned int eventMask = NSLeftMouseDownMask | NSLeftMouseUpMask
| NSLeftMouseDraggedMask | NSMouseMovedMask;
while (eventType != NSLeftMouseUp) {
NSEvent *event;
event = [NSApp nextEventMatchingMask: eventMask
untilDate: [NSDate distantFuture]
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
eventType = [event type];
if (eventType == NSLeftMouseDragged) {
[self mouseDragged: event];
}
}
} // runEventTrackingRunLoop
</pre>
(and thanks to Rainer Brockerhof for pointing out a no-op line of code from the original version of this)<p>
<li> <b>Performing relative (grab-hand) scrolling</b> (NSView->General)<br>
Grab-hand scrolling is a handy feature in several apps. It's nice being able to scroll around a large document diagonally, fer instance. Here's one way to do it (assuming you have a standard NSScrollView -> NSClipView -> YourView setup)
<p>
<pre>@interface Blah : NSView
{
NSPoint grabOrigin;
NSPoint scrollOrigin;
}
@end // Blah
...
@implementation Blah
...
- (void) mouseDown: (NSEvent *) event
{
// deal in window coordinates. there is a scrolling problem
// if using view coordinates because view coordinates
// can get transformed
grabOrigin = [event locationInWindow];
NSClipView *contentView;
contentView = (NSClipView*)[layerView superview];
scrollOrigin = [contentView bounds].origin;
} // mouseDown
- (void) mouseDragged: (NSEvent *) event
{
NSPoint mousePoint;
mousePoint = [event locationInWindow];
float deltaX, deltaY;
deltaX = grabOrigin.x - mousePoint.x;
deltaY = mousePoint.y - grabOrigin.y;
NSPoint newOrigin;
newOrigin = NSMakePoint (scrollOrigin.x + deltaX,
scrollOrigin.y + deltaY);
[layerView scrollPoint: newOrigin];
} // mouseDragged
...
@end // Blah
</pre>
You can be fancy and check for the option key by look at <code>[event modifierFlags]</code> and looking for <code>NSAlternateKeyMask</code>, and also use the NSCursor open/closedHandCursor.<p>
<li> <b>Reacting to the Escape key</b> (NSView->General)<br>
There's no predefined constant for the Escape key. Luckily the escape key predates every personal computer, and so it has a pretty reliable character value that's generated: 27:
<pre>- (void) keyDown: (NSEvent *) event
{
NSString *chars = [event characters];
unichar character = [chars characterAtIndex: 0];
if (character == 27) {
NSLog (@"ESCAPE!");
}
} // keyDown</pre><p>
<li> <b>Reacting to the delete key</b> (NSView->General)<br>
Look for <code>NSDeleteCharacter</code> in the event's character string:
<pre>- (void) keyDown: (NSEvent *) event
{
NSString *chars = [event characters];
unichar character = [chars characterAtIndex: 0];
if (character == NSDeleteCharacter) {
NSLog (@"Delete!");
}
} // keyDown</pre><p>
<li> <b>Responding to modifier keys only</b> (NSView->General)<br>
NSView's keyDown: and keyUp: don't fire when just a modifier key goes down. If you do want to react to this, you'll need to override <code>flagsChanged: (NSEvent *) event</code> and look at the event there. (flagsChanged: comes from NSResponder, so any responder in the responder chain can react to it)<p>
<li> <b>Setting a cursor for a view</b> (NSView->General)<br>
Sometimes you want to change the cursor for a view, say with a crosshair for a view that lets you select a rectangle out of an image. There is no <code>-[NSView setCursor:]</code> call (drat!) You can get something similar by adding this to your NSView subclass:
<pre>- (void) resetCursorRects
{
[super resetCursorRects];
[self addCursorRect: [self bounds]
cursor: [NSCursor crosshairCursor]];
} // resetCursorRects</pre><p>
<li> <b>Outlining every view</b> (NSView->Debugging)<br>
You can set the NSShowAllViews default value to have every view outlined. Nested views get outlined in different colors.
<p>
Set it "permanently" via
<pre>% defaults write com.apple.TextEdit NSShowAllViews YES</pre>
(or whatever your application identifier is), or use the one-shot version:
<pre>/Developer/Applications/Xcode.app/Contents/MacOS/Xcode -NSShowAllViews YES</pre><p>
<li> <b>Becoming firstResponder if you don't have an editable cell</b> (NSView->Hacks)<br>
You make a custom view, add it to your window in Interface Builder, and you make it the first responder. When you run the program, your custom view is <b>not</b> the first responder, instead some lame-O textfield has snagged keyboard focus. To address this, override this undocumented method:<pre>
- (BOOL) _hasEditableCell
{
return (YES);
} // _hasEditableCell
</pre>
This workaround is necessary at least for Mac OS X 10.1.*.<p>
<li> <b>Implementing -isFlipped in Swift</b> (NSView->Swift)<br>
Trying to do an override or other methody way of isFlipped results in the usual inscrutible Swift error messages. Do it this way instead:
<pre>
override var flipped: Bool {
return true
}
</pre><p>
<li> <b>Access environment variables from Cocoa</b> (Random->General)<br>
Check out NSProcessInfo's <code>environment</code> method<p>
<li> <b>Adding Growl Notifications</b> (Random->General)<br>
It's pretty easy adding growl notifications to your app. Download the SDK from <a href="http://growl.info/">growl.info</a>. Set up your Xcode project to copy the <code>Growl.framework</code> into your application bundle.
<p>
Pick a class to be the contact point with Growl. Your <code>AppController</code> class is a good place. Import <code><Growl/Growl.h></code>
<p>
Set the delegate to the <code>GrowlApplicationBridge</code>:<pre>
[GrowlApplicationBridge setGrowlDelegate: self];</pre>
Doing this will eventually have the <code>registrationDictionaryForGrowl</code> delegate message called. Return a dictionary with two arrays (Which can be the same). These are the names of the alerts you will be posting. These are human-readable, so you'll want to use a localized string (which I've already set in the global variable here:
<pre>
- (NSDictionary *) registrationDictionaryForGrowl
{
NSArray *notifications;
notifications = [NSArray arrayWithObject: g_timesUpString];
NSDictionary *dict;
dict = [NSDictionary dictionaryWithObjectsAndKeys:
notifications, GROWL_NOTIFICATIONS_ALL,
notifications, GROWL_NOTIFICATIONS_DEFAULT, nil];
return (dict);
} // registrationDictionaryForGrowl
</pre>
And use this to post a notification:
<pre>
[GrowlApplicationBridge notifyWithTitle: @"Woop! Time has expired!"
description: @"You have been waiting for 37 minutes"
notificationName: g_timesUpString
iconData: nil
priority: 0
isSticky: NO
clickContext: nil];
</pre>
Consult the SDK documentation for more explanations of the features, but they are pretty self-explanitory.<p>
<li> <b>Converting a Fixed to a float</b> (Random->General)<br>
<pre>float blah = FixedToFloat(someFixedvalue);</pre>
other goodies in <code>FixMath.h</code><p>
<li> <b>Determining current localization you're running in.</b> (Random->General)<br>
You can use this to find which of the user's preferred localizations/locales that is also supported by your app. (translation, what localization you're currently running in)
<pre>NSString *languageused = [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex: 0];</pre>
(thanks to Glenn Fawcett for this one!)<p>
<li> <b>Disabling Chimera's url completion pop-up</b> (Random->General)<br>
Add this to your <code>~/Library/Application Support/Chimera/Profiles/default/xyz.slt/prefs.js</code> file:<p>
<code>user_pref("browser.urlbar.autocomplete.enabled", false);</code><p>
<li> <b>Duration of a movie file</b> (Random->General)<br>
Got some movie files on disk living at a particular URL? Need to know the duration of the video?
<pre>
- (NSTimeInterval) timeOfVideoAtURL: (NSURL *) url {
AVPlayerItem *movieItem = [AVPlayerItem playerItemWithURL: url];
CMTime duration = movieItem.duration;
Float64 seconds = CMTimeGetSeconds (duration);
return (NSTimeInterval)seconds;
} // timeOfVideoAtURL
</pre><p>
<li> <b>Easy NSLogging of basic data structures</b> (Random->General)<br>
<code>NSString</code> has a couple of convenience functions for print out basic structures like <code>NSRect</code> and <code>NSSize</code>:
<ul>
<li> <code>NSStringFromRect (someNSRect);</code>
<li> <code>NSStringFromPoint (someNSPoint);</code>
<li> <code>NSStringFromSize (someNSSize);</code>
</ul><p>
<li> <b>Finding things like ~/Library, and ~/Library/Application Services</b> (Random->General)<br>
<code>NSSearchPathForDirectoriesInDomains</code> is how you find the location of things like Library directories, or User directories, document directory, and the like (this is the <code>NSSearchPathDirectory</code>). The <code>NSSearchPathDomainMask</code> is what domains to find things in. For instance for a <code>NSLibraryDirectory</code>, a <code>NSUserDomainMask</code> will give you the path to <code>~/Library</code>, <code>NSSystemDomainMask</code> will give you the path to <code>/System/Library</code>, and so on.
<p>
The directories inside of Library, like "Preferences" and "Application Support" are in English in the file system, and the Finder presents localized versions to the user. If you need <code>~/Library/Application Support/Borkware</code>, you can construct it like<pre>
NSMutableString *path;
path = [[NSMutableString alloc] init];
// find /User/user-name/Library
NSArray *directories;
directories = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
NSUserDomainMask, YES);
// if you had more than one user domain, you would walk directories and
// work with each path
[path appendString: [directories objectAtIndex: 0]];
[path appendString: @"/Application Support"];
[path appendString: @"/Borkware"];
</pre><p>
<li> <b>Getting the current user's name</b> (Random->General)<br>
<code>NSUserName()</code> or <code>NSFullUserName()</code>.
<p>
Thanks to Peter Hosey for letting us know about a better API for getting these.<p>
<li> <b>Putting an image on a disk image's window</b> (Random->General)<br>
To have snazzy disk images that have your logo or picture of your dog appear when you open them, do this:
<ul>
<li> run disk copy, make a new blank image. I'm calling mine "thingie"
<li> copy image file to the mounted fake drive (<code>logo.jpg</code>)
<li> open fake drive, choose Show View Options. Make sure you're just changing the current window, and not Everything.
<li> Choose "picture", select the <code>logo.jpg</code> that's on the thingie drive
<li> resize your window as appropriate
<li> run this in the terminal to hide the <code>logo.jpg</code> file<br>
<code>/Developer/Tools/SetFile -a V /Volumes/thingie/logo.jpg</code>
<li> eject thingie and re-mount it
<li> you should now see your image, and no visible picture file icon
</ul><p>
<li> <b>Reducing the size of a .dmg disk image</b> (Random->General)<br>
DiskCopy won't let you create a disk image of less than 5 megabytes (a bit inconvenient for packaging a program that's 30K). <code>Image->Convert Image</code> will let you compress the image. As a side-effect of the compressed format, this makes the image read-only.<p>
<li> <b>Reducing verbiage on web pages to make them readable.</b> (Random->General)<br>
In #macsb IRC one day I was complaining about a rumor site blog posting that was hugely long and nearly incomprehensible. I was pointed to the summarization service as a way of reducing the brainpower needed to figure out what was going on:
<ul>
<li> Select all the text
<li> Go to the services menu and select summarization service
<lI> You'll get a window with a slider at the bottom. Move that down all the way
to the left
<li>Amazingly, with 9/10 of blog posts it works REALLY well.
</ul>
(thanks to Chris Forsythe for this one)<p>
<li> <b>Seeing unread email in your gmail inbox</b> (Random->General)<br>
To see unread stuff in your gmail inbox search for this:
<pre>
in:inbox is:unread
</pre><p>
<li> <b>Setting environment variables for the gui login session</b> (Random->General)<br>
For environment variables you want set for the user's GUI login session, make a plist called <code>~/.MacOSX/environment.plist</code>. Make the root a Dictionary, and add the key/value pairs (all strings) to it. Don't forget to logout and back in.<p>
<li> <b>Turning off Spotlight</b> (Random->General)<br>
<code>% sudo mdutil -i off /</code><p>
<li> <b>minimal tool</b> (Random->General)<br>
I got tired of looking for this everytime I make a simple one-off tool without using Project Builder:
<pre>
#import <Foundation/Foundation.h>
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[pool release];
} // main
</pre>
<p>
And you can compile it on the command line with:
<pre>
cc -o test -framework Foundation file-name.m
</pre><p>
<li> <b>Disabling smooth scrolling in mountain lion</b> (Random->Hacks)<br>
I dislike smooth scrolling - I always get disoriented in Safari, plus I get a little seasick. You can turn it off by whacking a user default: (at least in 10.8):
<pre>% defaults write -g NSScrollAnimationEnabled -bool NO</pre><p>
<li> <b>Extracting PDF text on the cheap</b> (Random->Hacks)<br>
Sometimes you need the text from a PDF, and copy-pasting from Preview isn't terribly reliable. If you really just need text and no formatting, Spotlight can help you out:
<pre>% /usr/bin/mdimport -d2 ../Book.pdf >& oopack.txt</pre>
And edit out the little bit of extra metadata output.<p>
<li> <b>Control + trackpad zoom in Lion</b> (Random->Random)<br>
It took awhile to find where to enable the "zoom into the screen with control+scrollwheel":
System Preferences > Universal Access > Seeing > Zoom in window > Options > Use scroll wheel with modifier keys to zoom<p>
<li> <b>Fixing strange duplicate symbols</b> (Random->Random)<br>
While compiling OpenSP, the SGML parser for <a href="http://openjade.sourceforge.net/">OpenJade</a>, I got these errors:
<pre>
ld: multiple definitions of symbol OpenSP::Text::~Text [in-charge]()
Entity.lo definition of OpenSP::Text::~Text [in-charge]() in section (__TEXT,__text)
Group.lo definition of OpenSP::Text::~Text [in-charge]() in section (__TEXT,__text)
ld: multiple definitions of symbol OpenSP::Text::~Text [not-in-charge]()
Entity.lo definition of OpenSP::Text::~Text [not-in-charge]() in section (__TEXT,__text)
Group.lo definition of OpenSP::Text::~Text [not-in-charge]() in section (__TEXT,__text)
Param.lo definition of OpenSP::Text::~Text [in-charge]() in section (__TEXT,__text)
Param.lo definition of OpenSP::Text::~Text [not-in-charge]() in section (__TEXT,__text)
</pre>
These are caused by the class in question (Text) not having an explicit destructor declared, so the compiler is creating one for us in each of the files. In Text.cxx, add<pre>
Text::~Text() { }
</pre>
and in Text.h, add<pre>
~Text();
</pre>
to the Text class.
(I got it to compile and link, so I didn't go back to check to see if just sticking <code>~Text() {}</code> in Text.h would work.)<p>
<li> <b>Fixing strange undefined link symbols</b> (Random->Random)<br>
While building OpenJade, I got these link errors:
<pre>
gcc -dynamiclib -o .libs/libogrove.0.0.1.dylib Node.lo LocNode.lo -lm -lc -install_name /usr/local/lib/libogrove.0.dylib
ld: Undefined symbols:
vtable for __cxxabiv1::__class_type_info
vtable for __cxxabiv1::__si_class_type_info
operator delete(void*)
operator new(unsigned long)
___cxa_pure_virtual
___gxx_personality_v0
/usr/bin/libtool: internal link edit command failed
</pre>
The link line needs to have "-lstdc++" added to the end. For OpenJade, edit the libtool script and add <code>-lstdc++</code> to the end <code>archive_cmds</code> line. (but leave it inside the last closing double-quote)<p>
<li> <b>Launch Services admin tool</b> (Random->Random)<br>
If you need to poke around <strike>Lunch</strike> Launch Services, use the <code>lsregister</code> tool that lives in <code>/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support</code><p>
<li> <b>Make Mail.app display plain text messages</b> (Random->Random)<br>
Many HTML emails I receive get rendered in Mail.app in tiny, tiny point sizes. To have Mail.app prefer to display plain text instead, quit Mail.app and run this:
<p>
<code>% defaults write com.apple.mail PreferPlainText -bool TRUE</code><p>
<li> <b>Putting an imac to sleep at the login box</b> (Random->Random)<br>
Press the power button.<p>
<li> <b>Sending email with .mac when port 25 is blocked</b> (Random->Random)<br>
Say you're at a conference, away from your usual ISP, and you're trying to send email, but all port 25 traffic is blocked. Say also you have .mac, for which you keep paying and paying and paying to have a reliable external email address, and sending email is also blocked.
.mac (and perhaps other places) use an alternate port (port 587 in .mac's case) which can be use to work around this. Now I can send email, and life is happy.<p>
<li> <b>Where to put files for the system-wide web server</b> (Random->Random)<br>
When you turn on web sharing, the OS starts up an Apache for you, returning a default "It works!" page when you visit it. You can put your own content in the webserver by dropping files into:
<pre>/Library/WebServer/Documents</pre><p>
<li> <b>Easier Development of Screen Savers</b> (Screen Savers->General)<br>
When you're writing a screen saver, do you find yourself often copying the basted thing to your <code>~/Library/Screen Savers</code> folder? If so, you can make a symbolic link that points to your screen saver bundle, and that will automatically get used whenever you engage the screen saver engine. e.g.
<pre>
% cd ~/Library/Screen Savers
% ln -s ~/Projects/MonkeySaver/build/Debug/MonkeySaver.saver .
</pre><p>
<li> <b>Finding my resources</b> (Screen Savers->General)<br>
To find resources (say an image) from within the screensaver, use NSBundle bundleForClass. So, to get atomsymbol.jpg you would do something like <pre>
NSBundle *bundle;
NSString *path;
bundle = [NSBundle bundleForClass: [self class]];
path = [bundle pathForResource: @"atomsymbol" ofType: @"jpg"];
image = [[NSImage alloc] initWithContentsOfFile: path];</pre><p>
<li> <b>How about a simple sample or two?</b> (Screen Savers->General)<br>
There's <a href="/quickies/files/simplesaver.tar.gz">SimpleSaver</a>, a minimal 'bounce a line around the screen' screensaver. <a href="/quickies/files/fullscreensaver.tar.gz">FullScreenSaver</a> is the same thing, but it bounces the line around the user's desktop instead of just a black screen. Also check out <a href="/products/borkware-drip">Drip</a>, a little more sophisticated (even has a configuration panel).<p>
<li> <b>How do I set the name that's displayed in the preference panel?</b> (Screen Savers->General)<br>
The "Product Name" setting on the Project Builder target has that. Change it by:
<ol>
<li> open the <code>Targets</code> pane in Project Builder
<li> double-click on your target
<li> go to the <code>Build Settings</code> tab
<li> Edit your <code>Product Name</code> under <code>General Settings</code>
</ol><p>
<li> <b>Where is my preference file?</b> (Screen Savers->General)<br>
The screen saver preference file lives in the
<code>~/Library/Preferences/ByHost</code> directory. The name of the file is the name that you pass to <code>defaultsForModuleWithName:</code>, followed by
<code>.someBigNumber.plist</code>.
<p>
So, on my machine, <pre> userPrefs = [ScreenSaverDefaults defaultsForModuleWithName: @"BWDrip"];</pre> creates a file
<code>/Users/bork/Library/Preferences/ByHost/BWDrip.0003931024a6.plist
</code><p>
<li> <b>Running a screensaver as your desktop background</b> (Screen Savers->Random)<br>
In a terminal, run<p>
<code>% /System/Library/Frameworks/ScreenSaver.framework/Resources/ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine -background &</code>
<p>
(all one command)<p>
<li> <b>Basics</b> (NSTimer->General)<br>
<pre>
- (void) handleTimer: (NSTimer *) timer
{
do some work here...
} // handleTimer
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
target: self
selector: @selector(handleTimer:)
userInfo: nil
repeats: YES];
</pre><p>
<li> <b>NSTimers and NSTerminateLater</b> (NSTimer->General)<br>
If your application has an open TCP connection to a server and you receive an <code>applicationShouldTerminate:</code> message, you're likely to want to return<code>NSTerminateLater</code> so that you can first gracefully shut down the connection before quitting.
<p>
There is a gotcha: returning <code>NSTerminateLater</code> stops the main runloop, so your <code>NSTimers</code> will stop working from that point on. Thus, if you were depending on a timer firing to finish up your shut-down process, you'll never see it, and you'll hang. The solution is to return <code>NSTerminateCancel</code>, then do whatever you need to do and then terminate yourself manually. (Thanks to Larry Gerndt for this one!)<p>
<li> <b>Using NSTimer</b> (NSTimer->Swift)<br>
<pre>
var sessionTimer: NSTimer?
...
sessionTimer =
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self,
selector: #selector(SessionEditorViewController.lubDub(_:)),
userInfo: nil, repeats: true)
...
func lubDub(timer: NSTimer) {
let elapsedTime = NSDate().timeIntervalSince1970 - startTime
print("(elapsedTime)")
}
...
// clean up when done
sessionTimer?.invalidate()
sessionTimer = nil
</pre><p>
<li> <b>Populating a dictionary with predefined values</b> (NSDictionary->General)<br>
dictionaryWithObjectsAndKeys is handy if you're setting up a defaults preference dictionary (or text attributes, or any dictionary) by hand:<pre>
defaults = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 25.0], @"density",
[NSNumber numberWithFloat: 40.0], @"rate",
nil];
</pre><p>
<li> <b>Changing a file to -kb after it's been added</b> (CVS->General)<br>
<code>% cvs admin -kb filename</code><p>
<li> <b>Checking .nib and .pbproj files into CVS</b> (CVS->General)<br>
You'll need to use the <code>cvswrappers</code> file provided in the devtools. The easiest thing to do is to copy it to your homedir and prepend a dot:<p>
<code>% cp /Developer/Tools/cvswrappers ~/.cvswrappers</code>
<p>
You can also add the contents of that fike to your repository.<p>
<code>.pbproj</code> files aren't handled by the cvswrappers by default. You'll need to add a line like this:<pre>*.pbproj -k 'b' -f '/Developer/Tools/cvs-unwrap %s' -t '/Developer/Tools/cvs-wrap %s %s' -m 'COPY'</pre><p>
<li> <b>Dealing with the past</b> (CVS->General)<br>
To check out the world as it was 3 days ago:<p>
<code>% cvs checkout -D "3 days ago" blork</code><p>
Or via a date (month/day/year)<p>
<code>% cvs checkout -D "10/17/2002" fnord</code><p>
or recently in time<p>
<code>% cvs diff -D "1 hour ago" ook.blah</code><p>
<li> <b>Getting cvs update to build new directories</b> (CVS->General)<br>
<code>% cvs update -d [whatever]</code><p>
<li> <b>Merging a Branch with new directories in CVS</b> (CVS->General)<br>
When merging a branch back into the trunk (using the update command) be sure to use the <code>-d</code> option. Otherwise CVS will just ignore any directories added in the branch.
<p>
<pre>% cvs update -Ad #switch to the trunk (if you haven't already)
% cvs update -d -j branch_name
[resolve conflicts]
% cvs commit -m "whatever"</pre>
<p>
(Courtesy of Jeremy Stein)<p>
<li> <b>Recursively adding directories</b> (CVS->General)<br>
To recursively add stuff in CVS:
<pre>
% find . -type d -print | grep -v CVS | xargs cvs add
% find . -type f -print | grep -v CVS | xargs cvs add
</pre>
The first adds the directories, the second adds the files. Filenames with spaces in them won't be added, and it won't do the Right Thing with <code>-kb</code> for binary files.<p>
<li> <b>Showing the log comments between a set of revisions</b> (CVS->General)<br>
<code>% cvs log -r1.2:1.5 multiprocessing.txt</code><p>
<li> <b>Basic NSURLConnection delegate methods</b> (NSURL->General)<br>
<pre>
- (void) connection: (NSURLConnection *) connection
didReceiveResponse: (NSURLResponse *) response;
- (void) connection: (NSURLConnection *) connection didReceiveData: (NSData *) data;
- (void) connection: (NSURLConnection *) connection didFailWithError: (NSError *) error;
- (void) connectionDidFinishLoading: (NSURLConnection *) connection;
</pre><p>
<li> <b>Loading a string from a website</b> (NSURL->General)<br>
(this will block until it loads)
<pre> NSURL *url;
NSData *data;
NSString *blork;
url = [NSURL URLWithString: @"http://borkware.com/hacks/random"];
data = [url resourceDataUsingCache: NO];
blork = [[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding];</pre><p>
<li> <b>Loading an image from a website</b> (NSURL->General)<br>
(this will block until it loads)<pre> NSURL *url;
NSData *data;
NSImage *blork;
url = [NSURL URLWithString: @"http://borkware.com/hacks/random-pic"];
data = [url resourceDataUsingCache: NO];
blork = [[NSImage alloc] initWithData: data];</pre><p>
<li> <b>Open an URL in iphone safari</b> (NSURL->General)<br>
<pre> NSURL *url = [NSURL URLWithString: @"http://cuteoverload.com/2006/09/12/xtreme_gluttony/"];
[[UIApplication sharedApplication] openURL: url];
</pre><p>
<li> <b>CGShading</b> (Graphics->General)<br>
CGShading lets you do color ramps. Setting one up is a little complicated, juggling a couple of CG data structures and writing the ramping function.
<pre> float domain[2] = { 0.0, 1.0 }; // 1-in function
float range[8] = { 0.0, 1.0, // N-out, RGBA
0.0, 1.0,
0.0, 1.0,
0.0, 1.0 };
CGFunctionCallbacks callbacks = { 0, evaluate, NULL };
CGFunctionRef shaderFunction;
shaderFunction = CGFunctionCreate (self, // info / rock / context
1, // # of inputs for domain
domain, // domain
4, // # of inputs for range
range, // range
&callbacks);
CGColorSpaceRef deviceRGB;
deviceRGB = CGColorSpaceCreateDeviceRGB ();
CGShadingRef shader;
shader = CGShadingCreateAxial (deviceRGB, // colorspace
cgpoint(start), // start of axis
cgpoint(end), // end of axis
shaderFunction, // shader, 1-n, n-out
NO, // extend start
NO); // extend end
CGContextSaveGState (context); {
NSRect bounds = [self bounds];
CGContextClipToRect (context, cgrect(bounds));
CGContextDrawShading (context, shader);
} CGContextRestoreGState (context);
[self drawSpotAt: start size: 4];
[self drawSpotAt: end size: 4];
CGFunctionRelease (shaderFunction);
CGColorSpaceRelease (deviceRGB);
CGShadingRelease (shader);</pre>
And the evaluator function is given an array of inputs and outputs. Use the in value(s) (which run from your domain's start-to-finish values) to generate the out values (which should be in the range's start-to-finish values):
<pre>static void evaluate (void *info, const float *in, float *out)
{
float thing;
thing = in[0];
out[0] = thing;
out[1] = thing;
out[2] = thing;
out[3] = 1.0;
} // evaluate</pre><p>
<li> <b>Clipping to a CGPath</b> (Graphics->General)<br>
<pre> CGPathRef border = ... get a path from somewhere;
CGContextRef context = UIGraphicsGetCurrentContext ();
CGContextSaveGState (context); {
CGContextAddPath (context, border);
CGContextClip (context);
// draw draw draw
} CGContextRestoreGState (context);
</pre><p>
<li> <b>Converting HSL to RGB</b> (Graphics->General)<br>
<pre>// Based on Foley and van Dam algorithm.
void ConvertHSLToRGB (const CGFloat *hslComponents, CGFloat *rgbComponents) {
CGFloat hue = hslComponents[0];
CGFloat saturation = hslComponents[1];
CGFloat lightness = hslComponents[2];
CGFloat temp1, temp2;
CGFloat rgb[3]; // "temp3"
if (saturation == 0) {
// Like totally gray man.
rgb[0] = rgb[1] = rgb[2] = lightness;
} else {
if (lightness < 0.5) temp2 = lightness * (1.0 + saturation);
else temp2 = (lightness + saturation) - (lightness * saturation);
temp1 = (lightness * 2.0) - temp2;
// Convert hue to 0..1
hue /= 360.0;
// Use the rgb array as workspace for our "temp3"s
rgb[0] = hue + (1.0 / 3.0);
rgb[1] = hue;
rgb[2] = hue - (1.0 / 3.0);
// Magic
for (int i = 0; i < 3; i++) {
if (rgb[i] < 0.0) rgb[i] += 1.0;
else if (rgb[i] > 1.0) rgb[i] -= 1.0;
if (6.0 * rgb[i] < 1.0) rgb[i] = temp1 + ((temp2 - temp1)
* 6.0 * rgb[i]);
else if (2.0 * rgb[i] < 1.0) rgb[i] = temp2;
else if (3.0 * rgb[i] < 2.0) rgb[i] = temp1 + ((temp2 - temp1)
* ((2.0 / 3.0) - rgb[i]) * 6.0);
else rgb[i] = temp1;
}
}
// Clamp to 0..1 and put into the return pile.
for (int i = 0; i < 3; i++) {
rgbComponents[i] = MAX (0.0, MIN (1.0, rgb[i]));
}
} // ConvertHSLToRGB
</pre><p>
<li> <b>Converting RGB to HSL</b> (Graphics->General)<br>
Some color manipulations are easier in HSL than RGB.
<pre>
// Based on Foley and van Dam algorithm.
void ConvertRGBToHSL (const CGFloat *rgbComponents, CGFloat *hslComponents) {
CGFloat red = rgbComponents[0];
CGFloat green = rgbComponents[1];
CGFloat blue = rgbComponents[2];
CGFloat maxColor = MAX (red, MAX (green, blue));
CGFloat minColor = MIN (red, MIN (green, blue));
CGFloat deltaColor = maxColor - minColor;
CGFloat hue, saturation;
CGFloat lightness = (maxColor + minColor) / 2.0;
// All the same means a gray color.
if (maxColor == minColor) {
saturation = 0.0;
hue = 0.0; // officially undefined with gray, but go ahead and zero out.
} else {
if (lightness < 0.5) saturation = deltaColor / (maxColor + minColor);
else saturation = deltaColor / (2.0 - deltaColor);
if (red == maxColor) hue = (green - blue) / deltaColor;
else if (green == maxColor) hue = 2.0 + (blue - red) / deltaColor;
else hue = 4.0 + (red - green) / deltaColor;
// H will be in the range of 0..6, convert to degrees.
hue *= 60.0;
// Convert to positive angle.
if (hue < 0) hue += 360.0;
}
// Clamp to legal values.
hslComponents[0] = MAX (0.0, MIN (360.0, hue));
hslComponents[1] = MAX (0.0, MIN (1.0, saturation));
hslComponents[2] = MAX (0.0, MIN (1.0, lightness));
} // ConvertRGBToHSL</pre><p>
<li> <b>Converting degrees to radians and back</b> (Graphics->General)<br>
Most programming interfaces that deal with angles talk in radians (2 pi radians in a circle), like <code>CGAffineTransformRotate</code>. I personally think better in degrees (360 degrees in a circle).
<p>
Convert degrees to radians by multiplying by <code>180 / pi</code>.
<p>
Convert radians to degrees by multiplying by <code>pi / 180</code>
<p>
I have a couple of #defines I stick into a common header for projects that need it;
<pre>#define BWDegToRad(d) ((d) * M_PI / 180.0)
#define BWRadToDeg(r) ((r) * 180 / M_PI)
</pre><p>
<li> <b>Creating a CGImageMask from a UIImage</b> (Graphics->General)<br>
<pre>
- (CGImageRef) newMaskFromImage: (UIImage *) image {
CGImageRef maskRef = image.CGImage;
CGImageRef mask2 = CGImageMaskCreate (CGImageGetWidth (maskRef),
CGImageGetHeight (maskRef),
CGImageGetBitsPerComponent (maskRef),
CGImageGetBitsPerPixel (maskRef),
CGImageGetBytesPerRow (maskRef),
CGImageGetDataProvider (maskRef),
NULL, NO); // provider, shouldInterpolate
return mask2;
} // newMaskFromImage
</pre><p>
<li> <b>Draw a string centered in a rectangle</b> (Graphics->General)<br>
<pre>
NSString *blah = @"Placeholder Pane";
NSSize size = [blah sizeWithAttributes: nil];
NSPoint startPoint;
startPoint.x = bounds.origin.x + bounds.size.width / 2 - size.width / 2;
startPoint.y = bounds.origin.y + bounds.size.height / 2 - size.height / 2;
[blah drawAtPoint: startPoint
withAttributes: nil];
</pre><p>
<li> <b>Drawing a gradient</b> (Graphics->General)<br>
<pre>- (void) drawGradientInRect: (CGRect) rect
colorSpace: (CGColorSpaceRef) colorSpcae
context: (CGContextRef) context {
CGFloat startComponents[4] = { 254.0 / 255.0, 254.0 / 255.0, 254.0 / 255.0, 1.0 };
CGFloat endComponents[4] = { 206.0 / 255.0, 206.0 / 255.0, 206.0 / 255.0, 1.0 };
CGColorRef startColor = CGColorCreate (colorSpace, startComponents);
CGColorRef endColor = CGColorCreate (colorSpace, endComponents);
NSArray *array = [NSArray arrayWithObjects: (id)startColor, (id)endColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors (colorSpace,
(CFArrayRef)array, NULL);
CGPoint endPoint = rect.origin;
endPoint.y += rect.size.height;
// Don't let the gradient bleed all over everything
CGContextSaveGState (context); {
CGContextClipToRect (context, rect);
CGContextDrawLinearGradient (context, gradient, rect.origin, endPoint, 0);
} CGContextRestoreGState (context);
CGGradientRelease (gradient);
CGColorRelease (startColor);
CGColorRelease (endColor);
} // drawGradientInRect
</pre><p>
<li> <b>Drawing a string centered in a rectangle</b> (Graphics->General)<br>
<pre>
- (void) drawText: (NSString *) text
centeredInRect: (CGRect) rect
font: (UIFont *) font {
CGSize textSize = [text sizeWithFont: font
constrainedToSize: rect.size
lineBreakMode: NSLineBreakByWordWrapping];
// Center text rect inside of |rect|.
CGRect textRect = CGRectMake (CGRectGetMidX(rect) - textSize.width / 2.0,
CGRectGetMidY(rect) - textSize.height / 2.0,
textSize.width, textSize.height);
[text drawInRect: textRect
withFont: font
lineBreakMode: NSLineBreakByWordWrapping
alignment: NSTextAlignmentCenter];
} // drawText
</pre><p>
<li> <b>Drawing outlined / filled text</b> (Graphics->General)<br>
I wanted to draw text like this:<br> <img src="http://borkware.com/quickies/files/shaded-outlined-text.png">
<p>
<pre>- (void) drawRect: (CGRect) rect {
NSString *string = @"132";
CGContextRef ctx = UIGraphicsGetCurrentContext ();
CGContextTranslateCTM (ctx, 0.0, self.bounds.size.height);
CGContextScaleCTM (ctx, 1.0, -1.0);
CGContextSelectFont (ctx, "Helvetica-Bold", 48.0, kCGEncodingMacRoman);
CGContextSetTextDrawingMode (ctx, kCGTextFillStroke);
// Draw a background to see the text on.
CGContextSetFillColorWithColor (ctx, [[UIColor yellowColor] CGColor]);
CGContextFillRect (ctx, self.bounds);
CGContextSetFillColorWithColor (ctx, [[UIColor blackColor] CGColor]);
CGContextSetStrokeColorWithColor (ctx, [[UIColor whiteColor] CGColor]);
CGContextSetLineWidth (ctx, 2);
CGContextSetShadow (ctx, CGSizeMake (3.0, 3.0), 2.5);
CGContextShowTextAtPoint (ctx, 50, 50, [string UTF8String], string.length);
} // drawRect
</pre>
(Thanks to <a href="http://supermegaultragroovy.com">Chris Liscio</a> for handy pointers)<p>
<li> <b>Getting a CGContext from Cocoaland</b> (Graphics->General)<br>
You can use CG (CoreGraphics) functions inside of a Cocoa view - pass the graphics port of the current context to the CG functions, like this:
<pre>#define cgrect(nsrect) (*(CGRect *)&(nsrect))
- (void) drawRect: (NSRect) rect
{
NSRect bounds = [self bounds];
NSGraphicsContext *cocoaContext = [NSGraphicsContext currentContext];
CGContextRef context = (CGContextRef)[cocoaContext graphicsPort];
CGContextSetLineWidth (context, 5.0);
CGContextBeginPath(context); {
CGContextAddRect (context, cgrect(bounds));
CGContextSetRGBFillColor (context, 1.0, 0.9, 0.8, 1.0);
} CGContextFillPath(context);
} // drawRect</pre><p>
<li> <b>Grabbing the bits from the screen</b> (Graphics->General)<br>
(Many thanks to Paul Sobolik for giving us this an updated and more reliable version)
<pre>- (NSImage *) captureScreenImageWithFrame: (NSRect) frame
{
// Fetch a graphics port of the screen
CGrafPtr screenPort = CreateNewPort ();
Rect screenRect;
GetPortBounds (screenPort, &screenRect);
// Make a temporary window as a receptacle
NSWindow *grabWindow = [[NSWindow alloc] initWithContentRect: frame
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreRetained
defer: NO
screen: nil];
CGrafPtr windowPort = GetWindowPort ([grabWindow windowRef]);
Rect windowRect;
GetPortBounds (windowPort, &windowRect);
SetPort (windowPort);
// Copy the screen to the temporary window
CopyBits (GetPortBitMapForCopyBits(screenPort),
GetPortBitMapForCopyBits(windowPort),
&screenRect,
&windowRect,
srcCopy,
NULL);
// Get the contents of the temporary window into an NSImage
NSView *grabContentView = [grabWindow contentView];
[grabContentView lockFocus];
NSBitmapImageRep *screenRep;
screenRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: frame];
[grabContentView unlockFocus];
NSImage *screenImage = [[NSImage alloc] initWithSize: frame.size];
[screenImage addRepresentation: screenRep];
// Clean up
[grabWindow close];
DisposePort(screenPort);
return (screenImage);
} // captureScreenImageWithFrame</pre><p>
<li> <b>Pouring color through a mask</b> (Graphics->General)<br>
Given a <a href="http://borkware.com/quickies/single?id=485">CG Image mask</a>, fill it with color.
<pre>
- (void) fillMask: (CGImageRef) mask withColor: (UIColor *) color
atOffset: (CGPoint) offset {
if (color == nil) color = [UIColor purpleColor]; // everybody loves purple
CGContextRef context = UIGraphicsGetCurrentContext ();
CGSize size = self.bounds.size;
CGContextSaveGState (context); {
CGContextTranslateCTM (context, offset.x, offset.y);
// Account for retina-sized graphics.
CGContextScaleCTM (context, size.width / CGImageGetWidth(mask),
size.height / CGImageGetHeight(mask));
CGRect rect = { {0.0, 0.0}, { CGImageGetWidth(mask), CGImageGetHeight(mask) } };
CGContextClipToMask (context, rect, mask);
[color set];
UIRectFill (rect);
} CGContextRestoreGState (context);
} // fillMask
</pre><p>
<li> <b>Push/pop CGContext state</b> (Graphics->General)<br>
<pre>CGContextRef context = ...;
...
CGContextSaveGState (context); {
[drawable drawWithContext: context
inRect: bounds
withMetrics: self.metrics]; // or whatever drawing you're doing
} CGContextRestoreGState (context);
</pre>
Edit: Gus Meuller of <a href="http://flyingmeat.com">Flying Meat</a> fame has a block-based utility that does this:
<pre>void FMCGContextHoldGState (CGContextRef context, void (^block)()) {
CGContextSaveGState(context); {
block ();
} CGContextRestoreGState(context);
}
</pre>
and if you're wanting to do something similar with <code>NSGraphicsContext</code>s:
<pre>void FMNSContextHoldGState (void (^block)()) {
[NSGraphicsContext saveGraphicsState]; {
block ();
} [NSGraphicsContext restoreGraphicsState];
}
</pre><p>
<li> <b>Rounded rect path</b> (Graphics->General)<br>
Returned path is refcounted, so you'll need to <code>CFRelease</code> when done.
<pre>
- (CGPathRef) newPathForRoundRect: (CGRect) rect
radius: (CGFloat) radius
strokeWidth: (CGFloat) strokeWidth {
// Fit the stroked path inside the rectangle.
rect.size.height -= strokeWidth;
rect.size.width -= strokeWidth;
rect.origin.x += strokeWidth / 2.0;
rect.origin.y += strokeWidth / 2.0;
CGMutablePathRef path = CGPathCreateMutable();
// The inner rect size gives us X/Y/W/H values for the parts of the rect
// that aren't on the curve.
CGRect innerRect = CGRectInset(rect, radius, radius);
CGFloat insideRight = innerRect.origin.x + innerRect.size.width;
CGFloat outsideRight = rect.origin.x + rect.size.width;
CGFloat insideBottom = innerRect.origin.y + innerRect.size.height;
CGFloat outsideBottom = rect.origin.y + rect.size.height;
CGFloat insideTop = innerRect.origin.y;
CGFloat outsideTop = rect.origin.y;
CGFloat outsideLeft = rect.origin.x;
CGPathMoveToPoint (path, NULL, innerRect.origin.x, outsideTop);
CGPathAddLineToPoint (path, NULL, insideRight, outsideTop);
CGPathAddArcToPoint (path, NULL, outsideRight, outsideTop,
outsideRight, insideTop, radius);
CGPathAddLineToPoint (path, NULL, outsideRight, insideBottom);
CGPathAddArcToPoint (path, NULL, outsideRight, outsideBottom,
insideRight, outsideBottom, radius);
CGPathAddLineToPoint (path, NULL, innerRect.origin.x, outsideBottom);
CGPathAddArcToPoint (path, NULL, outsideLeft, outsideBottom,
outsideLeft, insideBottom, radius);
CGPathAddLineToPoint (path, NULL, outsideLeft, insideTop);
CGPathAddArcToPoint (path, NULL, outsideLeft, outsideTop,
innerRect.origin.x, outsideTop, radius);
CGPathCloseSubpath (path);
return path;
} // newPathForRoundRect
</pre>
If you're not in CGLand (or not needing the same code to work on the desktop and device), there's also UIBezierPath's <code>-bezierPathWithRoundedRect:cornerRadius:</code> (for all corners), and <code>-bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:</code> (for some arbitrary subset of corners), and NSBezierPath's <code>-bezierPathWithRoundedRect:xRadius:yRadius:</code>. Muchos Thankos to Paul Collins for the reminder.<p>
<li> <b>Using CGLayers</b> (Graphics->General)<br>
CGLayer is a Tiger (and beyond) feature that will cache Quartz operations. Kind of like a PICT for CG. Make a new layer based on the context you're going to be drawing it in, then get a context from the layer, and draw into that. So something like this:
<pre>
- (void) makeLayerInContext: (CGContextRef) enclosingContext
{
layer = CGLayerCreateWithContext (enclosingContext, CGSizeMake(100, 100),
NULL); // options - unused in Tiger
CGContextRef context;
context = CGLayerGetContext (layer);
// .. and do your drawing
} // makeLayer
</pre>
And then to draw the layer at a particular point (like replicating it a bunch of different points as done here):
<pre>
for (i = 0; i < pointCount; i++) {
CGContextDrawLayerAtPoint (context, locations[i], layer);
}
</pre><p>
<li> <b>Using a stretchable UIImage</b> (Graphics->General)<br>
Make an image somewhere (like in your <code>-init</code>):
<pre>
_timeIndicatorImage = [[[UIImage imageNamed: @"ride-profile-time-indicator"]
stretchableImageWithLeftCapWidth: 0.0
topCapHeight: 1.0] retain];
</pre>
Then draw it. In this case it's a vertical indicator.
<pre>
- (void) drawTimeIndicatorInRect: (CGRect) rect {
CGFloat timeFraction = _time / _totalDuration;
CGFloat x = [self horizontalPositionForTimeFraction: timeFraction];
CGRect indicatorRect = CGRectMake (x, 0, 3.0, rect.size.height);
[_timeIndicatorImage drawInRect: indicatorRect];
} // drawTimeIndicatorInRect
</pre><p>
<li> <b>What are the iDevice Icons?</b> (Graphics->General)<br>
Apple's <a href="http://developer.apple.com/library/ios/#qa/qa1686/_index.html">Technical Q&A 1686</a> has the lowdown on the icons you need. As of July 2011, this is the set:
<pre>
Sizes are square. Assuming a universal app
512 iTunesArtwork iTunes. No .png extension
57 Icon.png AppStore, iPhone/Touch home screen. Required
114 Icon@2x.png iPhone4
72 Icon-72.png App Store, iPad. Required.
29 Icon-Small.png Settings on iPad and iPhone, Spotlight iPhone
50 Icon-Small-50.png Spotlight iPad
58 Icon-Small@2x.png Settings and Spotlight iPhone4
</pre>
Then in your info.plist, make an "Icon files" entry (an array), and list all the Icon*.png files.<p>
<li> <b>Breaking on <Error>: doClip: empty path.</b> (Graphics->Debugging)<br>
Sometimes you get the message <code><Error>: doClip: empty path.</code> written to your console, with no obvious place to put a breakpoint. To find out where you're doing something wrong, put a breakpoint on <code>CGPostError</code>.<p>
<li> <b>Turning a string into a path</b> (Graphics->NSString)<br>
<pre>
- (NSBezierPath *) makePathFromString: (NSString *) string
forFont: (NSFont *) font
{
NSTextView *textview;
textview = [[NSTextView alloc] init];
[textview setString: string];
[textview setFont: font];
NSLayoutManager *layoutManager;
layoutManager = [textview layoutManager];
NSRange range;
range = [layoutManager glyphRangeForCharacterRange:
NSMakeRange (0, [string length])
actualCharacterRange: NULL];
NSGlyph *glyphs;
glyphs = (NSGlyph *) malloc (sizeof(NSGlyph)
* (range.length * 2));
[layoutManager getGlyphs: glyphs range: range];
NSBezierPath *path;
path = [NSBezierPath bezierPath];
[path moveToPoint: NSMakePoint (20.0, 20.0)];
[path appendBezierPathWithGlyphs: glyphs
count: range.length inFont: font];
free (glyphs);
[textview release];
return (path);
} // makePathFromString
</pre><p>
<li> <b>Disabling "Return moves editing to next cell" in TableView</b> (NSTableView->General)<br>
When you edit cells in a tableview, pressing return, tab, or shift-tab will end the current editing (which is good), and starts editing the next cell. But of times you don't want that to happen - the user wants to edit an attribute of a given row, but it doesn't ever want to do batch changes to everything.
<p>
To make editing end, you need to subclass NSTableView and add code to catch the textDidEndEditing delegate notification, massage the text movement value to be something other than the return and tab text movement, and then let NSTableView handle things.
<pre>// make return and tab only end editing, and not cause other cells to edit
- (void) textDidEndEditing: (NSNotification *) notification
{
NSDictionary *userInfo = [notification userInfo];
int textMovement = [[userInfo valueForKey:@"NSTextMovement"] intValue];
if (textMovement == NSReturnTextMovement
|| textMovement == NSTabTextMovement
|| textMovement == NSBacktabTextMovement) {
NSMutableDictionary *newInfo;
newInfo = [NSMutableDictionary dictionaryWithDictionary: userInfo];
[newInfo setObject: [NSNumber numberWithInt: NSIllegalTextMovement]
forKey: @"NSTextMovement"];
notification =
[NSNotification notificationWithName: [notification name]
object: [notification object]
userInfo: newInfo];
}
[super textDidEndEditing: notification];
[[self window] makeFirstResponder:self];
} // textDidEndEditing</pre>
(Thanks to Steven Jacowski for a tweak that ends editing on clicks on different cells)<p>
<li> <b>Dragging feedback only between rows</b> (NSTableView->General)<br>
When rearranging rows in a tableview, the user feedback should just go between rows (the whole-row highlighting is confusing). In the drop validation method only let things through for <code>NSTableViewDropAbove</code><pre>- (NSDragOperation) tableView: (NSTableView*) tableView
validateDrop: (id <NSDraggingInfo>) info
proposedRow: (int) row
proposedDropOperation: (NSTableViewDropOperation) op
{
int result = NSDragOperationNone;
if (op == NSTableViewDropAbove) {
result = NSDragOperationMove;
}
return (result);
} // validateDrop
</pre><p>
<li> <b>Handling double-clicks</b> (NSTableView->General)<br>
Use <Code>NSTableView</code>'s <code>-setDoubleAction:</code> method, and supply it a standard IBAction-style method selector.
<p>
You may need to also do <code>-setTarget:</code>. double-clicks get sent if the column isn't editable, so you may need to grab the column and do a <code>-setEditable: NO</code> on it.<p>
<li> <b>Making a table view a drag source</b> (NSTableView->General)<br>
If you want to make your NSTableView a source for a drag and drop operation, you need to implement
<pre>
- (BOOL) tableView: (NSTableView *) tableView
writeRowsWithIndexes: (NSIndexSet *) rowIndexes
toPasteboard: (NSPasteboard *) pboard;
</pre>
in your table view data source (and don't forget to hook up the datasource if you use bindings to populate the tableview)
<p>
(Thanks to Rob Rix for updating this with more modern API)<p>
<li> <b>Preventing the return key from editing the next cell</b> (NSTableView->General)<br>
The default tableview behavior, when editing a row, is to move the field editor to the next cell when the user commits editing using the Return key. If you want Return to commit the editing and dismiss the field editor, subclass NSTableView and add this:<pre>- (void) textDidEndEditing: (NSNotification *) notification
{
NSDictionary *userInfo;
userInfo = [notification userInfo];
NSNumber *textMovement;
textMovement = [userInfo objectForKey: @"NSTextMovement"];
int movementCode;
movementCode = [textMovement intValue];
// see if this a 'pressed-return' instance
if (movementCode == NSReturnTextMovement) {
// hijack the notification and pass a different textMovement
// value
textMovement = [NSNumber numberWithInt: NSIllegalTextMovement];
NSDictionary *newUserInfo;
newUserInfo = [NSDictionary dictionaryWithObject: textMovement
forKey: @"NSTextMovement"];
notification = [NSNotification notificationWithName:
[notification name]
object: [notification object]
userInfo: newUserInfo];
}
[super textDidEndEditing: notification];
} // textDidEndEditing
</pre><p>
<li> <b>Putting the ordering triangles in table columns</b> (NSTableView->General)<br>
Put this somewhere (maybe in your <code>mouseDownInHeaderOfTableColumn</code> or <code>didClickTableColumn</code>:
<pre>
NSImage *indicatorImage;
if (sortAscending) {
<i>sort your data ascending</i>
indicatorImage = [NSImage imageNamed: @"NSAscendingSortIndicator"];
} else {
<i>sort your data descending</i>
indicatorImage = [NSImage imageNamed: @"NSDescendingSortIndicator"];
}
sortAscending = !sortAscending;
[tableView setIndicatorImage: indicatorImage
inTableColumn: tableColumn];
[tableView reloadData];
</pre><p>
<li> <b>Responding to selection changes</b> (NSTableView->General)<br>
<pre>- (void) tableViewSelectionDidChange: (NSNotification *) notification
{
int row;
row = [tableView selectedRow];
if (row == -1) {
<i>do stuff for the no-rows-selected case</i>
} else {
<i>do stuff for the selected row</i>
}
} // tableViewSelectionDidChange</pre>
If you have more than one tableview, the notification's object is the tableview that had the selection change.<p>
<li> <b>Table View drag destination on or between rows</b> (NSTableView->General)<br>
When your NSTableView is a drag destination, you may want to support both dragging onto existing objects (to replace them, or augment them with some new attribute the user is dragging over) and support dragging between existing objects (to insert a new one in between. To get this behavior, you need to use NSTableView's <code>-setDropRow:dropOperation:</code>, like so:
<pre>- (NSDragOperation) tableView: (NSTableView *) view
validateDrop: (id <NSDraggingInfo>) info
proposedRow: (int) row
proposedDropOperation: (NSTableViewDropOperation) op
{
// have the table highlight on-row / between-row correctly
[view setDropRow: row
dropOperation: op];
// or use whatever drag operation is appropriate
NSDragOperation dragOp = NSDragOperationCopy;
return (dragOp);
} // validateDrop</pre>
and in the <code>acceptDrop</code> method, look at the operation:
<pre>- (BOOL) tableView: (NSTableView *) view
acceptDrop: (id <NSDraggingInfo>) info
row: (int) row
dropOperation: (NSTableViewDropOperation) op
{
if (op == NSTableViewDropOn) {
// replace existing
} else if (op == NSTableViewDropAbove) {
// add new
} else {
NSLog (@"unexpected operation (%d) in %s",
op, __FUNCTION__);
}
// documentation doesn't actually say what this signifies
return (YES);
} // acceptDrop</pre><p>
<li> <b>Tableview DataSource methods</b> (NSTableView->General)<br>
Your datasource needs to implement these two bad boys:
<p><code>
- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView<br>
- (id) tableView: (NSTableView *) tableView<br>
objectValueForTableColumn: (NSTableColumn *) tableColumn<br>
row: (NSInteger) row<br>
</code><p>
<li> <b>Add pgplsql support</b> (Postgresql->General)<br>
<code>% createlang plpgsql template1</code><p>
<li> <b>Adding a column to a table</b> (Postgresql->General)<br>
<code>alter table pfo_survey_response_2006 add column section text</code><p>
<li> <b>Backing up a database</b> (Postgresql->General)<br>
<code>% pg_dump databasename > db.out</code><p>
<li> <b>Connecting to a user / database</b> (Postgresql->General)<br>
Say you have your primary database user as 'nsadmin', and they have a database called 'openacs'. If you're using the default <code>pg_hba.conf</code>, you can connect to the database, logged in as an ordinary person, by doing<p>
<code>% psql -U nsadmin opeancs</code><p>
<li> <b>Creating a new database</b> (Postgresql->General)<br>
<code>% createdb database-name</code><p>
<li> <b>Creating a new user</b> (Postgresql->General)<br>
<code>psql=# create user bork with password 'bork';</code>
<p>
You can also do this for a passwordless user:
<code>% crateuser bork</code><p><p>
<li> <b>Inserting a time column</b> (Postgresql->General)<br>
If you have the minutes, seconds, and am/pm and want to get them into a time column (time without timezone), you can do something like this:<p>
<pre>psql=# select to_timestamp('10:45am', 'HH12:MIam')::timetz::time;
to_timestamp
--------------
10:45:00
(1 row)
</pre><p>
<li> <b>Limiting selections to X number of rows</b> (Postgresql->General)<br>
use the "<code>limit</code>" statement:
<pre>select stuff from some_table
where stuff.blah > 12
limit 5
</pre><p>
<li> <b>Logging in as a specific user</b> (Postgresql->General)<br>
<code>% psql database-name user-name</code><p>
<li> <b>Logging into a remote database</b> (Postgresql->General)<br>
On machine tower, dbname borkdb, user nettest<br>
<code>% psql -h tower borkdb nettest</code><p>
<li> <b>Manually starting the database</b> (Postgresql->General)<br>
<code>% pg_ctl -D /usr/local/pgsql/data -l /tmp/pgsql.log start</code><p>
<li> <b>Next value from a sequence</b> (Postgresql->General)<br>
<pre>insert into blah (id, stuff)
values (nextval('sequence_name'), 'stuff');</pre><p>
<li> <b>Restoring a Database</b> (Postgresql->General)<br>
<code>% psql -d databasename -f db.out</code><p>
<li> <b>Seeing available databases</b> (Postgresql->General)<br>
<code>% psql -l</code><br>
(lower case Ell)<p>
<li> <b>Selecting a random row from a table</b> (Postgresql->General)<br>
use the <code>random()</code> function.<pre>
select stuff.src, stuff.width, stuff.height from
(select src, width, height,
random()
from bw_eyes
order by 4) stuff
limit 1
</pre>
This selects a random row from <code>bw_eyes</code> and returns the interesting data from it. This query is used on the quickies pages to randomly select an image of eyes staring back.<p>
<li> <b>Time interval math to integer</b> (Postgresql->General)<br>
If you take the difference between two timestamps, you can end up with an unweildly value like "00:06:14.37086 days old". You can cast the timestamps into just regular dates like:<br>
<code>select current_timestamp()::date - entry_date::date as days_old from kb_nuggets;
</code><p>
<li> <b>Turning off the psql pager</b> (Postgresql->General)<br>
<code>wplug-oacs=# <b>\pset pager</b><br>
Pager usage is off.
</code><p>
<li> <b>Using tree_sortkey</b> (Postgresql->General)<br>
Jade Rubick has some brief <a href="http://rubick.com:8002/openacs/tree_sortkey">documentation</a> on using the postgresql tree_sortkey stuff.<p>
<li> <b>pg interval math</b> (Postgresql->General)<br>
Older versions of pg would support <code>select blah from whatever where date-column > (current_timestamp - 21)</code> to get all the blahs from whatever in the last 21 days. This doesn't work in newer versions of Postgresql. Instead, you need to use a time interval:<pre>
select to_char (whenx, 'fmDD fmMonth YYYY') as pretty_when, text
from blog
where whenx > (current_timestamp - interval '21 days')
order by whenx desc
</pre><p>
<li> <b>Increasing memory buffer sizes</b> (Postgresql->Administration)<br>
If you try upping your <code>shared_buffers</code> in your <code>postgresql.conf</code>, and get an error similar to this:<pre>
IpcMemoryCreate: shmget(key=5432001, size=266371072, 03600) failed:
Invalid argument
This error usually means that PostgreSQL's request for a shared memory
segment exceeded your kernel's SHMMAX parameter.
</pre>
You should increase your the <code>shmmax</code> parameters using tips in the <a href="http://borkware.com/quickies/one?topic=Unix">Unix administration quickies</a>.<p>
<li> <b>Darwin Website</b> (Darwin->General)<br>
Since I can never remember this, it's at <a href="http://developer.apple.com/darwin/">developer.apple.com/darwin</a>.<p>
<li> <b>Darwin's CVSROOT string</b> (Darwin->CVS)<br>
for read-only checkouts:
<p>
<code>setenv CVSROOT :pserver:<i>username</i>@anoncvs.opensource.apple.com:/cvs/Darwin</code><p>
<li> <b>Appending to the end of a textview</b> (NSTextView->General)<br>
<pre> NSRange range;
range = NSMakeRange ([[textView string] length], 0);
[textView replaceCharactersInRange: range withString: string];</pre>
And Peter Hosey provided a one-liner that may suit your needs better:
<pre>
[[[textView textStorage] mutableString] appendString: string];
</pre><p>
<li> <b>Deleting the selected text</b> (NSTextView->General)<br>
<code>[textView delete: nil]</code><p>
<li> <b>Determining the paragraph style at a particular point</b> (NSTextView->General)<br>
<pre>- (NSParagraphStyle *) paragraphStyleInTextView: (NSTextView *) textView
atIndex: (int) index
{
NSTextStorage *storage = [textView textStorage];
NSDictionary *attributes;
NSRange effectiveRange;
attributes = [storage attributesAtIndex: index
effectiveRange: &effectiveRange];
NSParagraphStyle *style;
style = [attributes valueForKey: NSParagraphStyleAttributeName];
return (style);
} // paragraphStyleInTextView</pre><p>
<li> <b>Finding the span of a paragraph</b> (NSTextView->General)<br>
The NSString paragraphRangeForRange: method gives you the span of a paragraph. If you're using the text architecture, you need to get the string from the text storage. This chunklet shows the paragraph span for the current selection:
<pre> NSRange selectedRange = [textview selectedRange];
NSTextStorage *storage = [textview textStorage];
effectiveRange = [[storage string] paragraphRangeForRange: selectedRange];</pre><p>
<li> <b>Forcing validation of pending text field entries</b> (NSTextView->General)<br>
<code>[window makeFirstResponder: window]</code><p>
<li> <b>Moving insertion point to the end</b> (NSTextView->General)<br>
<code>NSRange range = { [[textView string] length], 0 };<br>
[textView setSelectedRange: range];</code><p>
<li> <b>Moving insertion point to the start</b> (NSTextView->General)<br>
<code>NSRange zeroRange = { 0, 0 };<br>
[textView setSelectedRange: zeroRange];</code><p>
<li> <b>Replacing NSTextView's contents with an attributed string</b> (NSTextView->General)<br>
<pre> [[textView textStorage] setAttributedString: theNewString];</pre><p>
<li> <b>Restricting typed in text to just digits</b> (NSTextView->General)<br>
Create a subclass of <code>NSNumberFormatter</code>, add this method, and hook up the formatter to your text fields.
<pre>- (BOOL) isPartialStringValid: (NSString **) partialStringPtr
proposedSelectedRange: (NSRangePointer) proposedSelRangePtr
originalString: (NSString *) origString
originalSelectedRange: (NSRange) origSelRange
errorDescription: (NSString **) error
{
NSCharacterSet *nonDigits;
NSRange newStuff;
NSString *newStuffString;
nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
newStuff = NSMakeRange(origSelRange.location,
proposedSelRangePtr->location
- origSelRange.location);
newStuffString = [*partialStringPtr substringWithRange: newStuff];
if ([newStuffString rangeOfCharacterFromSet: nonDigits
options: NSLiteralSearch].location != NSNotFound) {
*error = @"Input is not an integer";
return (NO);
} else {
*error = nil;
return (YES);
}
} // isPartialStringValid</pre><p>
<li> <b>Scrolling to the end of a textview</b> (NSTextView->General)<br>
<pre> NSRange range;
range = NSMakeRange ([[textView string] length], 0);
[textView scrollRangeToVisible: range];</pre>
I've heard that <code>scrollRangeToVisible</code> is O(N) for the length of the text. So be on the lookout if you have lots of text involved.<p>
<li> <b>Show NSTextField text in gray if the field is disabled</b> (NSTextView->General)<br>
NSTextField doesn't change its text color if the field is enabled or disabled (?) Even more bizarre, using NSColor's <code>disabledControlTextColor</code> won't draw the text in the disabled color. You need to use the <code>secondarySelectedControlColor</code>, which supposedly is for active controls that don't have focus. Go figure.
<p>
To do it yourself, subclass NSTextField and override <code>setEnabled:</code> to change the color:
<pre>- (void) setEnabled: (BOOL) flag
{
[super setEnabled: flag];
if (flag == NO) {
[self setTextColor: [NSColor secondarySelectedControlColor]];
} else {
[self setTextColor: [NSColor controlTextColor]];
}
} // setEnabled</pre>
<p>
This actually kind of a gross workaround for a Cocoa bug - the disabled color is getting made darker rather than lighter. The secondarySelectedControlColor ends up looking disabled by a happy coincidence that it starts out lighter before being darkened. (or something like this. UberDude Dave MacLachlan has <A href="http://www.cocoabuilder.com/archive/message/cocoa/2006/7/21/168028">done the legwork to figure out the underlying problem</a>.)<p>
<li> <b>Truncating text in the middle of a string</b> (NSTextView->General)<br>
Sometimes you see text that is of the form "Some Gro...eral Fish", with the middle truncated but the ends still readable. You can get this effect yourself with:
<pre>
[[textfield cell] setLineBreakMode: NSLineBreakByTruncatingMiddle]
</pre>
(Thanks to <a href="http://www.red-sweater.com/">Daniel Jalkut</a> for this one)<p>
<li> <b>scrolling to the beginning of a textview</b> (NSTextView->General)<br>
<code>NSRange zeroRange = { 0, 0 };<br>
[textView scrollRangeToVisible: zeroRange];</code><p>
<li> <b>Getting the contents of a TextView as an NSString</b> (NSTextView->NSString)<br>
<pre>NSString *commitMessage;
commitMessage = [[commitTextView textStorage] string];</pre><p>
<li> <b>Changing checkbox toggle state</b> (NSControl->General)<br>
<code>[myCheckbox setState: NSOffState]</code><p>
<code>NSOnState</code> also works.<p>
<li> <b>Respond to every keystroke in a textfield</b> (NSControl->General)<br>
Make your object a delegate of the textfield, and add this NSControl delegate method:<br>
<pre>
- (void) controlTextDidChange: (NSNotification *) notification
{
// do work here, like count the characters or something
} // controlTextDidBeginEditing
</pre><p>
<li> <b>'join' an array of strings into a single string</b> (NSString->General)<br>
<pre>NSArray *chunks = ... get an array, say by splitting it;
string = [chunks componentsJoinedByString: @" :-) "];</pre>
would produce something like
<pre>oop :-) ack :-) bork :-) greeble :-) ponies
</pre><p>
<li> <b>'split' a string into an array</b> (NSString->General)<br>
<pre> NSString *string = @"oop:ack:bork:greeble:ponies";
NSArray *chunks = [string componentsSeparatedByString: @":"];</pre><p>
<li> <b>Converting string to an integer</b> (NSString->General)<br>
</code>NSString *string = ...;<br>
int value = [string intValue];</code>
<p>
Similarly, there is a <code>floatValue</code> and <code>doubleValue</code> <code>NSString</code> methods.<p>
<li> <b>Counting number of words in a string</b> (NSString->General)<br>
<pre>-(NSUInteger) jacWordCount: (NSString *) string {
__block NSUInteger wordCount = 0;
[string enumerateSubstringsInRange:NSMakeRange(0, string.length)
options:NSStringEnumerationByWords
usingBlock:^(NSString *character, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
wordCount++;
}];
return wordCount;
}
</pre>
(Muchos thankos to Jared Crawford for a more modern implementation)<p>
<li> <b>Iterating attributes in an attributed string</b> (NSString->General)<br>
This prints out each of the attribute runs from an attributed string:
<pre>
NSAttributedString *string = ...;
NSRange totalRange = NSMakeRange (0, string.length);
[string enumerateAttributesInRange: totalRange
options: 0
usingBlock: ^(NSDictionary *attributes, NSRange range, BOOL *stop) {
NSLog (@"range: %@ attributes: %@",
NSStringFromRange(range), attributes);
}];
</pre>
and if you're in 10.5 or earlier:
<pre>- (void) iterateAttributesForString: (NSAttributedString *) string
{
NSDictionary *attributeDict;
NSRange effectiveRange = { 0, 0 };
do {
NSRange range;
range = NSMakeRange (NSMaxRange(effectiveRange),
[string length] - NSMaxRange(effectiveRange));
attributeDict = [string attributesAtIndex: range.location
longestEffectiveRange: &effectiveRange
inRange: range];
NSLog (@"Range: %@ Attributes: %@",
NSStringFromRange(effectiveRange), attributeDict);
} while (NSMaxRange(effectiveRange) < [string length]);
} // iterateAttributesForString</pre><p>
<li> <b>Making localizable strings</b> (NSString->General)<br>
You will need a file named <code>Localizable.strings</code> that lives in your <code>English.lproj</code> directory (or whatever localization directory is appropriate). It has this syntax:
<pre>"BorkDown" = "BorkDown";
"Start Timer" = "Start Timer";
"Stop Timer" = "Stop Timer";</pre>
That is, a key followed by a localized value.
<p>
In your code, you can then use <code>NSLocalizedString()</code> or one of its variants:
<pre> [statusItem setTitle: NSLocalizedString(@"BorkDown", nil)];</pre>
The second argument is ignored by the function. Obstensively it is a <code>/* comment */</code> in the strings file so that you can match the key back to what it is supposed to actually be.<p>
<li> <b>NSLog without the extra crud</b> (NSString->General)<br>
<code>NSlog</code> puts too much crud in front of the logging line. For a foundation tool that output stuff, it gets in the way. I'd still like for a replacement to expand <code>%@</code>, which the <code>printf()</code> family won't do. Here's a some code that'll do that.<pre>
#include <stdarg.h>
void LogIt (NSString *format, ...)
{
va_list args;
va_start (args, format);
NSString *string;
string = [[NSString alloc] initWithFormat: format arguments: args];
va_end (args);
fprintf (stderr, "%s\n", [string UTF8String]);
[string release];
} // LogIt
</pre><p>
<li> <b>Putting an image into an attributed string</b> (NSString->General)<br>
You'll need to use a text attachment.
<pre>- (NSAttributedString *) prettyName
{
NSTextAttachment *attachment;
attachment = [[[NSTextAttachment alloc] init] autorelease];
NSCell *cell = [attachment attachmentCell];
NSImage *icon = [self icon]; // or wherever you are getting your image
[cell setImage: icon];
NSString *name = [self name];
NSAttributedString *attrname;
attrname = [[NSAttributedString alloc] initWithString: name];
NSMutableAttributedString *prettyName;
prettyName = (id)[NSMutableAttributedString attributedStringWithAttachment:
attachment]; // cast to quiet compiler warning
[prettyName appendAttributedString: attrname];
return (prettyName);
} // prettyName</pre>
This puts the image at the front of the string. To put the image in the middle of the string, you'll need to create an attributedstring with attachment, and then append that to your final attributed string.<p>
<li> <b>Removing all spaces from a string</b> (NSString->General)<br>
<pre>
NSString *filebase = [baseName stringByReplacingOccurrencesOfString: @" "
withString: @""];
</pre><p>
<li> <b>Stripping out newlines from a string</b> (NSString->General)<br>
So you have an NSString and want to yank out the newlines. You can do a <a href="http://borkware.com/quickies/single?id=358">split</a>and <a href="http://borkware.com/quickies/single?id=360">join</a>, like in scripting languages, or you can make a mutable copy and manipulate that:
<pre> NSMutableString *mstring = [NSMutableString stringWithString:string];
NSRange wholeShebang = NSMakeRange(0, [mstring length]);
[mstring replaceOccurrencesOfString: @"
"
withString: @""
options: 0
range: wholeShebang];
return [NSString stringWithString: mstring];</pre>
(this can also be used for generic string manipulations, not just stripping out newlines).
<p>
This technique takes half the time (at least) of split/join. But probably not enough to make an impact. In a simple test, split/join took 0.124 seconds to strip 36909 newlines in a 1.5 meg textfile, and 0.071 seconds to do the same.<p>
<li> <b>Substring matching</b> (NSString->General)<br>
<code>NSRange range = [[string name] rangeOfString: otherString options: NSCaseInsensitiveSearch];</code><p>
<li> <b>Today's date as a string</b> (NSString->General)<br>
The general solution for converting a date to a string is NSDateFormatter. Sometimes you need to generate a date string in a particular format easily. For instance if you need "December 4, 2007", you can use:
<pre>[[NSDate date] descriptionWithCalendarFormat: @"%B %e, %Y" timeZone: nil locale: nil]</pre>
(Thanks to Mike Morton for this one)
<br>
(Also Mac OS X only)<p>
<li> <b>Trimming whitespace from ends of a string</b> (NSString->General)<br>
<pre>
NSString *ook = @"\n \t\t hello there \t\n \n\n";
NSString *trimmed =
[ook stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(@"trimmed: '%@'", trimmed);
</pre>
produces
<pre>
2009-12-24 18:24:42.431 trim[6799:903] trimmed: 'hello there'
</pre><p>
<li> <b>Truncating a string</b> (NSString->General)<br>
One way of truncating an NSString to a set length, and attach "..." if it got shortened.
<pre>- (NSString *) truncateString: (NSString *) string
toCharacterCount: (NSUInteger) count {
NSRange range = { 0, MIN(string.length, count) };
range = [string rangeOfComposedCharacterSequencesForRange: range];
NSString *trunc = [string substringWithRange: range];
if (trunc.length < string.length) {
trunc = [trunc stringByAppendingString: @"..."];
}
return trunc;
} // truncateString
</pre><p>
<li> <b>Put a string on the pasteboard</b> (NSString->Random)<br>
Here's a category for easily putting a string on the paste|clipboard:
<pre>
@implementation NSString (PasteboardGoodies)
- (void) sendToPasteboard
{
[[NSPasteboard generalPasteboard]
declareTypes: [NSArray arrayWithObject: NSStringPboardType]
owner:nil];
[[NSPasteboard generalPasteboard]
setString: self
forType: NSStringPboardType];
} // sendToPasteboard
@end // PasteboardGoodies
</pre>
Thanks to <a href="http://www.red-sweater.com">Dan Jalkut</a> for this tip.<p>
<li> <b>Draw a string in bold</b> (NSString->Graphics)<br>
<pre>- (void) drawLabel: (NSString *) label
atPoint: (NSPoint) point
bold: (BOOL) bold {
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
NSFont *currentFont = [NSFont userFontOfSize: 14.0];
if (bold) {
NSFontManager *fm = [NSFontManager sharedFontManager];
NSFont *boldFont = [fm convertFont: currentFont
toHaveTrait: NSBoldFontMask];
[attributes setObject: boldFont
forKey: NSFontAttributeName];
} else {
[attributes setObject: currentFont
forKey: NSFontAttributeName];
}
[label drawAtPoint: point withAttributes: attributes];;
} // drawLabel
</pre><p>
<li> <b>Processing every line in a string</b> (NSString->NSArray)<br>
Say you got a string that's composed of one interesting data item per line, like <code>/usr/share/dict/words</code> or from a script, and you want to process them. In this case turning a bunch of strings into floats:
<pre>
NSMutableArray *altitudes = [NSMutableArray array];
NSString *altitudeString = [self altitudeStringFromGoogle: coords];
[altitudeString enumerateLinesUsingBlock: ^(NSString *line, BOOL *stop) {
float value = [line floatValue];
[altitudes addObject: [NSNumber numberWithFloat: value]];
}];</pre><p>
<li> <b></b> (NSString->Swift)<br>
Truncating a string with the first N characters. Good as of Swift 3.
<pre>
func truncate(string: String, toCount count: Int) -> String {
if let endIndex = string.index(string.startIndex,
offsetBy: count,
limitedBy: string.endIndex) {
let range = string.startIndex ..< endIndex
let result = string[range]
if result == string { // It's exactly |count| thingies big
return string
} else {
return result + "..."
}
}
return string
}
</pre><p>
<li> <b>Getting a document's window</b> (NSDocument->General)<br>
For 10.3 and later systems, use <code>-windowForSheet</code> (which doesn't tell you the window associated with a particular sheet, but instead returns the window to be used for showing sheets, which is usually what you want when talking about the document's window - Thanks to Peter Hosey for this one).
<p>
For folks stuck in 10.2 land, or who want to do it themselves, there's always this:
<pre>- (NSWindow *) window
{
NSArray *windowControllers;
windowControllers = [self windowControllers];
NSWindowController *controller;
controller = [windowControllers objectAtIndex: 0];
NSWindow *window;
window = [controller window];
return (window);
} // window</pre><p>
<li> <b>Increasing the length of the "Open Recent" menu</b> (NSDocument->General)<br>
10-15 items not enough for your "Open Recent" menu of a favorite app? Set the app's NSRecentDocumentsLimit default to something more to your liking:
<pre>defaults write com.flyingmeat.acorn NSRecentDocumentsLimit 137</pre>
(thanks to Gus Mueller for this one)<p>
<li> <b>Keeping backup files of documents</b> (NSDocument->General)<br>
override NSDocument's <code>keepBackupFile</code>:
<pre>- (BOOL) keepBackupFile
{
return (YES);
} // keepBackupFile
</pre>
This will do the tilde thing to the older version of the file.<p>
<li> <b>Mapping a window to its document</b> (NSDocument->General)<br>
<pre> NSDocumentController *documentController;
documentController = [NSDocumentController sharedDocumentController];
BWBorkStitchDocument *noteDocument;
noteDocument = [documentController documentForWindow: noteWindow];</pre><p>
<li> <b>Storing window position in the document</b> (NSDocument->General)<br>
If you want to store the window position in your document, you'll need to store and restore the <code>[window frame]</code> of the window in question. (That's left as an exercise to the reader, based on how you're storing your document contents.) When restoring the window location, you have to turn of <code>NSWindowController</code>'s cascading, otherwise it'll honor your width and height, but not your origin. In your document's <code>windowControllerDidLoadNib</code> method, you'll need:<pre>
[[self windowController] setShouldCascadeWindows: NO];
NSWindow *window;
window = [self window];
[window setFrame: loadedWindowBounds display: YES];
</pre><p>
<li> <b>Using a custom title for NSWindowController windows</b> (NSDocument->NSWindow)<br>
When using utility windows in a document-based app, NSWindowController is handy for handling a lot of the grungy details. When using a shared window (like an inspector window), you want its title to reflect that of the document. Override windowTitleForDocumentDisplayName to set the title. In this case, it becomes of the form "Overview of Untitled 2":
<pre>- (NSString *) windowTitleForDocumentDisplayName: (NSString *) displayName
{
NSString *string;
string = [NSString stringWithFormat: @"Overview of %@", displayName];
return (string);
} // windowTitleForDocumentDisplayName</pre><p>
<li> <b>Window Position Autosave for NSWindowController-owned inspectors</b> (NSDocument->NSWindow)<br>
It's nice to autosave the position of inspector windows so that they come backup where the user put them. Unfortunately, NSWindowController wipes out the autosave name that's set in Interface Builder. You have to set it in code in your window controller subclass. The windowDidLoad method (which is used instead of awakeFromNib) is a handy place:
<pre>- (void) windowDidLoad
{
[super windowDidLoad];
[self setShouldCascadeWindows: NO];
[self setWindowFrameAutosaveName: @"pannerWindow"];
// and any other windowDidLoad work to be done
} // windowDidLoad
</pre><p>
<li> <b>Enabling and Disabling menu items</b> (NSMenu->General)<br>
Under most circumstances, it happens automatically: a menu item will be disabled if there is no object in the responder chain that handles the menu's action. If you want to override this behavior or extend it further, override <code>validateMenuItem:</code> and do something like this:
<pre>
- (BOOL) validateMenuItem: (id <NSMenuItem>) menuItem
{
BOOL result = YES;
if ([menuItem action] == @selector(deleteNote:)) {
if ([notes count] == 1) {
result = NO; // can't delete the last note
}
} else if ([menuItem action] == @selector(gotoNote:)) {
if ([notes count] == 1) {
result = NO; // can't go to a different not if only one
}
}
return (result);
} // validateMenuItem
</pre><p>
<li> <b>Running code before a menu action is sent</b> (NSMenu->General)<br>
The <code>NSMenuWillSendActionNotification</code> is posted to the notification center before the action is invoked.<p>
<li> <b>Cheap profiling</b> (Unix API->General)<br>
You can use <code>gettimeofday()</code> to get sub-second granularity for timing:<pre>
#include <sys/time.h>
struct timeval start, end;
...
gettimeofday (&start, NULL);
... the code you're timing
gettimeofday (&end, NULL);
double fstart, fend;
fstart = (start.tv_sec * 1000000.0 + start.tv_usec) / 1000000.0;
fend = (end.tv_sec * 1000000.0 + end.tv_usec) / 1000000.0;
NSLog (@"it took %f seconds", fend - fstart);
</pre><p>
<li> <b>Making a timestamp with strftime</b> (Unix API->General)<br>
<pre> time_t now;
now = time(NULL);
struct tm *tm;
tm = localtime (&now);
char timestamp[200];
strftime (timestamp, 200, "%m/%d/%Y-%H:%M", tm);
</pre><p>
<li> <b>Handling double-clicks in a browser</b> (NSBrowser->General)<br>
<pre>- (void) awakeFromNib
{
[browser setTarget: self];
[browser setDoubleAction: @selector(browserDoubleClick:)];
} // awakeFromNib
...
- (IBAction) browserDoubleClick: (id) sender
{
int column = [browser selectedColumn];
int row = [browser selectedRowInColumn: column];
// then dig into your data structure with the row and column
} // browserDoubleClick</pre><p>
<li> <b>Finding things like user's Document directory.</b> (NSFIleManager->General)<br>
<code>NSFileManager</code>'s URLForDirectory is how you can find the location of things like the user's Documents directory. The domain is what domain, like NSDocumentDirectory, NSUserDomainMask will give you the URL to the iOS sandbox for your app's user files. <code>appropriateForURL</code> is is advanced - mainly of use if you're going to be doing an atomic swap of files.
<p>
In Swift 4:
<pre>
let fm = FileManager()
do {
let documentDirectoryURL = try fm.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true)
// use documentDirectoryURL
} catch {
// handle error as you see fit
}
</pre>
ObjC:
<pre>
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error;
NSURL *localDocumentDirectory =
[fm URLForDirectory: NSDocumentDirectory
inDomain: NSUserDomainMask
appropriateForURL: nil
create: YES
error: &error];
if (localDocumentDirectory == nil) { // never compare error == nil
NSLog (@"could not url for directory, dude - %@", error);
}
</pre>
(looking how to <a href="http://borkware.com/quickies/single?id=199">do it with paths</a>? )<p>
<li> <b>Getting directory listing based on URL</b> (NSFIleManager->General)<br>
<pre>
NSURL *mediaDirectory = ...;
NSArray *contents = [fm contentsOfDirectoryAtURL: mediaDirectory
includingPropertiesForKeys: @[]
options: 0
error: &error];
if (contents == nil) {
NSLog (@"could not contents of %@ - %@", mediaDirectory, error);
return nil;
}
</pre>
There's also <code>enumeratorAtURL:includingPropertiesForKeys:...</code> that gives you back a directory enumerator.<p>
<li> <b>Moving a file to a tilde file</b> (NSFIleManager->General)<br>
<pre> NSString *filename = @"/my/original/file/name";
NSString *tildeFilename;
tildeFilename = [NSString stringWithFormat: @"%@~", filename];
// remove it first, otherwise the move will fail
[defaultManager removeFileAtPath: tildeFilename
handler: nil];
// now rename the file
[defaultManager movePath: filename
toPath: tildeFilename
handler: nil];</pre>
If you want to stick the tidle before the file extension (so the finder could open <code>ook~.tiff</code>), try this:
<pre> NSString *pathExtension = [filename pathExtension];
if (!pathExtension) {
tildeFilename = [filename stringByAppendingString: @"~"];
} else {
tildeFilename = [NSString stringWithFormat: @"%@~.%@", [filename stringByDeletingPathExtension], pathExtension];
}</pre>
(Thanks to Peter Hosey for this one)
</pre><p>
<li> <b>Removing a file</b> (NSFIleManager->General)<br>
<pre> NSFileManager *defaultManager;
defaultManager = [NSFileManager defaultManager];
[defaultManager removeFileAtPath: tildeFilename
handler: nil];</pre>
The handler is an object that will be sent message, like <code>fileManager:shouldProceedAfterError:</code> if something goes wrong during the removal.<p>
<li> <b>Saving an NSData to a file</b> (NSFIleManager->General)<br>
<pre> NSString *filename = @"/this/is/my/file/name";
NSData *data = // get NSData from somewhere, like NSPropertyListSerialization
[data writeToFile: filename atomically: NO];</pre><p>
<li> <b>(re)indent an XML file</b> (XML->General)<br>
<pre>
% setenv XMLLINT_INDENT " "
% xmllint --format oopack.xml > blah.xml
</pre><p>
<li> <b>Reading property lists from XML</b> (XML->General)<br>
This will read property lists in XML (or any of the other property list formats:)
<pre>
NSData *data = // get NSData from somewhere, like NSFileManager
if (data) {
myRootObject = [NSPropertyListSerialization
propertyListFromData: data
mutabilityOption: NSPropertyListMutableContainers
format: nil
errorDescription: nil];
}
</pre>
For the mutability options of the resulting object, you can also use <code>NSPropertyListImmutable</code> and <code>NSPropertyListMutableContainersAndLeaves</code><p>
<li> <b>Saving property lists as XML</b> (XML->General)<br>
You can save any of the "property list" classes (<code>NSDictionary</code>, <code>NSArray</code>,<code>NSNumber</code>, <code>NSString</code>, <code>NSData</code>) as XML like this:
<pre> NSData *data;
data = [NSPropertyListSerialization
dataFromPropertyList: notes
format: NSPropertyListXMLFormat_v1_0
errorDescription: nil];</pre>
Then write out the data using <code>NSFileManager</code>, or whatever other mechanism you wish.<p>
<li> <b>Archiving a document using keyed archiving</b> (NSCoder->General)<br>
This uses the new Tiger NSDocument load/store. For real code you'll want to handle errors and create an appropriate NSError object. This also saves the document in an XML format.
<pre>
- (NSData *) dataOfType: (NSString *) typeName
error: (NSError **) error
{
*error = nil;
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver;
archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData: data];
[archiver setOutputFormat: NSPropertyListXMLFormat_v1_0];
[archiver encodeObject: stitches forKey: @"stitches"];
// and archive other stuff you want
[archiver finishEncoding];
[archiver release];
return ([data autorelease]);
} // dataOfType
- (BOOL) readFromData: (NSData *) data
ofType: (NSString *) typeName
error: (NSError **) error
{
*error = nil;
NSKeyedUnarchiver *archiver;
archiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData: data];
[stitches release];
stitches = [archiver decodeObjectForKey: @"stitches"];
// decode other stuff of interest
[stitches retain];
return (YES);
} // readFromData</pre><p>
<li> <b>Decoding one class as another</b> (NSCoder->General)<br>
If you change the name of a class, and then try to use NSUnarchiver to expand an archived stream, you'll get an error like this (in this case "BWRawPath" was renamed to "BWSymbol"):
<p>
<code>StitchEdit[3522] *** class error for 'BWRawPath': class not loaded</code>
<p>
In a convenient place (like in the +load method of your class), use NSUnarchiver's <code>decodeClassName:asClassName:</code>
<pre>@implementation BWSymbol
+ (void) load
{
[NSUnarchiver decodeClassName: @"BWRawPath"
asClassName: @"BWSymbol"];
// No need to [super load] - the superclass +load has already
// been invoked automatically by the runtime.
} // load</pre>
Note that this won't help you if you're wanting to rename something that was added via <code>encodeValueOfObjCType:</code>. You'll have to write some code to unarchive your data using the old <code>@encode(oldName)</code> and then re-archive it as <code>@encode(newName)</code>
<p>
(Thanks to Greg Miller for spotting an error in this quickie)<p>
<li> <b>Simple archiving</b> (NSCoder->General)<br>
To archive a single object (which can be something like an array or dictionary that contains other object), such as saving your document, do:<pre>
- (NSData *) dataRepresentationOfType: (NSString *) aType
{
NSData *data;
data = [NSArchiver archivedDataWithRootObject: group];
return (data);
} // dataRepresentationOfType
</pre><p>
<li> <b>Simple unarchiving</b> (NSCoder->General)<br>
To unarchive a single object (which can be something like an array or dictionary that contains other object), such as loading your document, do:<pre>
- (BOOL) loadDataRepresentation: (NSData *) data
ofType: (NSString *) aType
{
// replace the moedl
[group release];
group = [NSUnarchiver unarchiveObjectWithData: data];
[group retain];
// make any associations to hook the new data into your document
return (YES);
} // loadDataRepresentation
</pre><p>
<li> <b>Supporting NSKeyedArchiving in your objects</b> (NSCoder->General)<br>
They should conform to the <code>NSCoding</code> protocol.
<pre>- (void) encodeWithCoder: (NSCoder *) coder
{
[coder encodeInt: x forKey: @"x"];
[coder encodeInt: y forKey: @"y"];
[coder encodeObject: color1 forKey: @"color1"];
[coder encodeObject: color2 forKey: @"color2"];
[coder encodeInt: direction forKey: @"direction"];
} // encodeWithCoder
- (id) initWithCoder: (NSCoder *) coder
{
if (self = [super init]) {
x = [coder decodeIntForKey: @"x"];
y = [coder decodeIntForKey: @"y"];
color1 = [coder decodeObjectForKey: @"color1"];
color2 = [coder decodeObjectForKey: @"color2"];
direction = [coder decodeIntForKey: @"direction"];
}
return (self);
} // initWithCoder</pre><p>
<li> <b>Supporting decoding</b> (NSCoder->General)<br>
<pre>- (id) initWithCoder: (NSCoder *) aDecoder
{
if (self = [super init]) {
// or else [super initWithCoder: aDecoder] if your superclass
// supports it. NSObject does not.
// decoding a C type
[aDecoder decodeValueOfObjCType: @encode(int)
at: &itemCount];
// decoding an array of C structs
if (items != NULL) {
free (items);
}
items = malloc (sizeof(BWRawPathItem) * itemCount);
int i;
for (i = 0; i < itemCount; i++) {
[aDecoder decodeValueOfObjCType: @encode(BWRawPathItem)
at: &items[i]];
}
// decoding an object
name = [aDecoder decodeObject];
[name retain];
// any other random initializations that don't come from
// the encoder
fooby = -1;
}
return (self);
} // initWithCoder</pre><p>
<li> <b>Supporting encoding</b> (NSCoder->General)<br>
<pre>- (void) encodeWithCoder: (NSCoder *) aCoder
{
// [super encodeWithCoder:] if you inherit from a class
// that supports encoding. NSObject does not.
// encoding a C type
[aCoder encodeValueOfObjCType: @encode(int)
at: &itemCount];
// encoding an array of C structs
int i;
for (i = 0; i < itemCount; i++) {
[aCoder encodeValueOfObjCType: @encode(BWRawPathItem)
at: &items[i]];
}
// encoding an object
[aCoder encodeObject: name];
} // encodeWithCoder</pre><p>
<li> <b>Launching a task</b> (NSTask->General)<br>
Here are the basics to launch "ls -l -a -t" in the current directory, and then read the result into an NSString:
<pre>
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: @"/bin/ls"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: @"-l", @"-a", @"-t", nil];
[task setArguments: arguments];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
NSData *data;
data = [file readDataToEndOfFile];
NSString *string;
string = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
NSLog (@"woop! got\n%@", string);</pre>
Of course, you can use different <code>NSFileHandle</code> methods for different styles of reading, and you can make a pipe for standard input so you can feed data to the task.<p>
<li> <b>Performing complex pipelines.</b> (NSTask->General)<br>
You can create multiple <code>NSTasks</code> and a bunch of <code>NSPipes</code> and hook them together, or you can use the "<code>sh -c</code>" trick to feed a shell a command, and let it parse it and set up all the IPC. This pipeline cats /usr/share/dict/words, finds all the words with 'ham' in them, reverses them, and shows you the last 5.
<pre> NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: @"/bin/sh"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: @"-c",
@"cat /usr/share/dict/words | grep -i ham | rev | tail -5", nil];
[task setArguments: arguments];
// and then do all the other jazz for running an NSTask.</pre>
<i>One important note:</i>, don't feed in arbitrary user data using this trick, since they could sneak in extra commands you probably don't want to execute.<p>
<li> <b>Stripping a string of all HTML tags via a perl script and NSTask</b> (NSTask->General)<br>
Here is a little perl script called <code>stripper.pl</code> which removes everything that looks like an HTML / SGML / XML tag:
<blockquote><pre>
#!/usr/bin/perl
while (<>) {
$_ =~ s/<[^>]*>//gs;
print $_;
}
<pre></blockquote>
Be sure to chmod +x the script. Add it to your project, add a new "Copy Files" phase, and have it stick this script into the Executables directory.
<p>
This method will take a string and feed it through the perl script:
<pre>- (NSString *) stringStrippedOfTags: (NSString *) string
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *stripperPath;
stripperPath = [bundle pathForAuxiliaryExecutable: @"stripper.pl"];
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: stripperPath];
NSPipe *readPipe = [NSPipe pipe];
NSFileHandle *readHandle = [readPipe fileHandleForReading];
NSPipe *writePipe = [NSPipe pipe];
NSFileHandle *writeHandle = [writePipe fileHandleForWriting];
[task setStandardInput: writePipe];
[task setStandardOutput: readPipe];
[task launch];
[writeHandle writeData: [string dataUsingEncoding: NSASCIIStringEncoding]];
[writeHandle closeFile];
NSMutableData *data = [[NSMutableData alloc] init];
NSData *readData;
while ((readData = [readHandle availableData])
&& [readData length]) {
[data appendData: readData];
}
NSString *strippedString;
strippedString = [[NSString alloc]
initWithData: data
encoding: NSASCIIStringEncoding];
[task release];
[data release];
[strippedString autorelease];
return (strippedString);
} // stringStrippedOfTags</pre><p>
<li> <b>Using sockets with cocoa</b> (Samples->General)<br>
<a href="/quickies/files/networkolizer.tar.gz">networkolizer.tar.gz</a> is a very short sample that opens a socket connection (using the Berkeley sockets API), and then writes an HTTP request, and reads the response.<p>
<li> <b>Adding your own objects to the responder chain</b> (NSObject->General)<br>
For I project I was working on, I needed the editing tools (for a graphical editor) to be in the responder chain so they could react to menu items. Doing this was surprisingly easy.
<p>
First step was to make the tools inherit from NSResponder:
<pre>@interface BWTool : NSResponder
{
// blah
}
// ... more blah
@end // BWTool</pre>
Then, in the view class where the tool gets set, put the tool in the responder chain by setting its next responder to be the view's current next responder. If a different tool gets set, take the current tool's next responder and give it to the new tool. Otherwise the view gets its original next responder back:
<pre>- (void) setTool: (BWTool *) newTool
{
NSResponder *nextResponder;
// this is the next element in the chain
if (currentTool != nil) {
nextResponder = [currentTool nextResponder];
} else {
nextResponder = [self nextResponder];
}
// decide who gets to point to the next responder
if (newTool != nil) {
// stick the tool into the chain
[self setNextResponder: newTool];
[newTool setNextResponder: nextResponder];
} else {
// cut the tool out of the chain (if there was one)
[self setNextResponder: nextResponder];
}
[newTool retain];
[currentTool release];
currentTool = newTool;
} // setDrawTool</pre>
And now tools can have action methods, and menu items that enable and disable appropriately.<p>
<li> <b>Apple's recommended retain/release technique</b> (NSObject->General)<br>
This way if "get" performance is important:
<pre>
- (NSString*) title
{
return (title);
} // title
- (void) setTitle: (NSString *) newTitle
{
[title autorelease];
title = [newTitle copy];
} // setTitle
</pre>
For maximum safety in the face of threads, use this:
<pre>
- (NSString *) title
{
return [[title retain] autorelease];
} // title
</pre>
This puts <code>title</code> into the current thread's autorelease pool, so <code>title</code> is protected from being destroyed by someone else in another thread.
<pre>
- (void) setTitle: (NSString *) newTitle
{
// use a mutex or NSLock to protect this
if (title != newtitle) {
[title release];
title = [newTitle copy];
}
} // setTitle
</pre>
By making a copy of the object, you're protected from another thread changing the value underneath you.<p>
<li> <b>Making a delegate</b> (NSObject->General)<br>
You've decided you want your class to have a delegate. Here's how you go about doing it:
<ol>
<li> Make an <code>id</code> instance variable in your class, and make methods to set and get it:<pre>
id delegate;
...
-(void) setDelegate: (id) del
{
delegate = del; // you don't need to retain it
} // setDelegate
- (id) delegate
{
return (delegate);
} // delegate
</pre><p>
<li> Make a category on <code>NSObject</code> so the compiler won't generate warnings, and also to document the delegate methods you support:<pre>
@interface NSObject(BWStitchViewDelegate)
- (BOOL) shouldHandleStuff: (NSData *) data;
@end
</pre><p>
<li> Check to make sure your delegate understands the message before sending it.<pre>
...
if ([delegate respondsToSelector: @selector(shouldHandleStuff:)]) {
BOOL blah;
blah = [delegate shouldHandleStuff: somedata];
if (blah) ...
}
</pre>
</ol><p>
<li> <b>Seeing the selectors objects ask you about</b> (NSObject->General)<br>
Sometimes it's handy to see when someone is asking your object what selectors it reponds to. Stick this in your class and you can see what folks are asking for:<pre>
- (BOOL) respondsToSelector: (SEL) aSelector
{
NSLog (@"%s", (char *) aSelector);
return ([super respondsToSelector: aSelector]);
} // respondsToSelector
</pre>
This takes advantage of an implementation detail where the <code>SEL</code> is actually a pointer to a C string<p>
<li> <b>Should you retain the delegate in your objects?</b> (NSObject->General)<br>
If you're creating an object, and you provide a delegate feature, should you retain the delegate? Nope! These objects are peers, not an owner/owned, or parent/child relationship. If you free your object, you shouldn't need to worry about memory management of your delegate. Likewise, if someone releases the delegate object, they should clean up after themselves and tell you to have a nil delegate.<p>
<li> <b>Sorting with Selectors</b> (NSObject->General)<br>
You can sort mutable arrays by providing a selector that is invoked on each of the elements of the array. I always get confused about NSOrderedAscending / NSOrderedDescending (I think they're actually backwards, but that's just me). Here it is in a nutcase:
Given this sort command: <pre>
[themStitches sortUsingSelector: @selector(compareByRowLocation:)];
</pre>
And this selector on the BWCrossStitch class:
<pre>
- (NSComparisonResult) compareByRowLocation: (BWCrossStitch *) thing2;
</pre>
Figure out which is lesser or greater, and return one of these. Note that <code>self</code> is the object's logical value, not the actual value of the <code>self</code> pointer.
<ul>
<lI> <b><code>NSOrderedAsending</code></b> if <code>self</code> < <code>thing2</code>
<lI> <b><code>NSOrderedDescending</code></b> if <code>self</code> > <code>thing2</code>
<li> <b><code>NSOrderedSame</code></b> if <code>self</code> == <code>thing2</code>
</ul><p>
<li> <b>My color well has a little triangle on it?</b> (NSColorWell->General)<br>
If your color well has a little triangle in the corner after you set the color programatically, that probably means the <code>NSColor</code> you used was created in Device space (e.g. <code>colorWithDeviceRed:green:blue:alpha:</code>) If you use a Calibrated color (<code>colorWithCalibratedRed:green:blue:alpha:</code>), the triangle goes away.<p>
<li> <b>Posting notifications.</b> (Notifications->General)<br>
In the header file:<p>
<code>extern NSString *BWStitchGroup_VisualAttributeChangeNotification;</code>
<p>
In the <code>.m</code> file:<p>
<code>NSString *BWStitchGroup_VisualAttributeChangeNotification<br>
= @"BWStitchGroup Visual Attribute Change Notification";</code><br>
<code>...</code><br>
<pre>NSNotificationCenter *center;
center = [NSNotificationCenter defaultCenter];
[center postNotificationName:
BWStitchGroup_VisualAttributeChangeNotification
object: self];</pre>
If you want to pass a userinfo dictionary, you can use a variant method:
<pre> NSDictionary *userInfo = ...;
[center postNotificationName: BWStitchGroup_VisualAttributeChangeNotification
object: self
userInfo: userInfo];
</pre><p>
<li> <b>Receiving notifications</b> (Notifications->General)<br>
<pre>NSNotificationCenter *center;
center = [NSNotificationCenter defaultCenter];
[center addObserver: self
selector: @selector(groupVisualChange:)
name: BWStitchGroup_VisualAttributeChangeNotification
object: group];
</pre>
Where the selector looks like this:
<pre>- (void) groupVisualChange: (NSNotification *) notification
{
// do stuff with the notification
} // groupVisualChange</pre><p>
<li> <b>Removing yourself from notifications</b> (Notifications->General)<br>
Don't forget to unregister yourself from the notification center in your <code>-dealloc</code>:
<pre> NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver: self];
</pre><p>
<li> <b>Watching all notifications</b> (Notifications->General)<br>
There's three notification centers - one for the app, one for the workspace, and the distributed notification center. Here's how to watch everything:
<pre>#import <Cocoa/Cocoa.h>
#import <stdio.h> // for printf()
@interface NotificationSpy
{
}
+ (void) startSpying;
+ (void) stopSpying;
@end // NotificationSpy
// prevent us from adding ourselves multiple times
static BOOL g_spying;
@implementation NotificationSpy
// turn on listening for all notifications.
+ (void) startSpying
{
if (!g_spying) {
NSNotificationCenter *center;
// first the default notification center, which is all
// notifications that just happen inside of our program
center = [NSNotificationCenter defaultCenter];
[center addObserver: self
selector: @selector(observeDefaultCenterStuff:)
name: nil
object: nil];
// then the NSWorkspace notification center, which tells us things
// like other applications launching, and the machine sleeping
// and waking
center = [[NSWorkspace sharedWorkspace]
notificationCenter];
[center addObserver: self
selector: @selector(observeWorkspaceStuff:)
name: nil
object: nil];
// and lastly the distributed notification center. This is a
// global (to the computer) notification center. You can find
// out when a different program gets focus, or the sound or
// screen brightness changes.
center = [NSDistributedNotificationCenter
notificationCenterForType: NSLocalNotificationCenterType];
[center addObserver: self
selector: @selector(observeDistributedStuff:)
name: nil
object: nil];
g_spying = YES;
}
} // startSpying
// remove us as observers
+ (void) stopSpying
{
if (!g_spying) {
NSNotificationCenter *center;
// take us off the default center for our app
center = [NSNotificationCenter defaultCenter];
[center removeObserver: self];
// and for the workspace
center = [[NSWorkspace sharedWorkspace]
notificationCenter];
[center removeObserver: self];
// and finally off of the machine-wide center
center = [NSDistributedNotificationCenter
notificationCenterForType: NSLocalNotificationCenterType];
[center removeObserver: self];
g_spying = NO;
}
} // stopSpying
+ (void) observeDefaultCenterStuff: (NSNotification *) notification
{
// QuietLog can also be found in the quickies
QuietLog (@"default: %@", [notification name]);
} // observeDefaultCenterStuff
+ (void) observeDistributedStuff: (NSNotification *) notification
{
QuietLog (@"distributed: %@", [notification name]);
} // observeDistributedStuff
+ (void) observeWorkspaceStuff: (NSNotification *) notification
{
QuietLog (@"workspace: %@", [notification name]);
} // observeWorkspaceStuff
@end // NotificationSpy</pre><p>
<li> <b>Register a notification with a block</b> (Notifications->Blocks)<br>
<pre> [center addObserverForName: kGRZoneSystem_ZoneSystemChangedNotification
object: nil
queue: [NSOperationQueue mainQueue]
usingBlock: ^(NSNotification *notification) {
[self adaptToZoneChange];
}];
</pre><p>
<li> <b>Converting .snd resource to aiff</b> (NSSound->General)<br>
Get <a href="http://www.spies.com/~franke/SoundApp/#download">SoundApp PPC</a> (a classic App). Choose <code>File->Convert</code>, set these attributes:
<ul>
<li> <b>Format</b> : AIFF
<li> <b>Encoding</b> : PCM
<li> <b>Rate</b> : 44100 Hz
<li> <b>Bits</b> : 16 bits
</ul>
and save. Then you can load it and play it with <code>NSSound</code>
<p>
Addendum: NSSound is based on Core Audio and QuickTime as of 10.3, so it may be able to play <code>.snd</code> resource files as-is. So give that a try if you don't need to support 10.2 or earlier. (Thanks to Peter Hosey for the clarification.)<p>
<li> <b>Converting xsd:date to NSDate</b> (NSDate->General)<br>
<code>xsd:date</code> has a format that's a subset of the full ISO8601 format. Here's a quick way to convert an <code>xsd:date</code> to an NSDate. Based on a forum posting by Jens Alfke. For a full ISO 8601 parser, check out <a href="http://boredzo.org/iso8601parser/">the one</a> by Peter Hosey.
<p>
Note that thiscode
<pre>
NSDate *xsdDateTimeToNSDate (NSString *dateTime) {
static NSDateFormatter *xsdDateTimeFormatter;
if (!xsdDateTimeFormatter) {
xsdDateTimeFormatter = [[NSDateFormatter alloc] init]; // Keep around forever
xsdDateTimeFormatter.timeStyle = NSDateFormatterFullStyle;
xsdDateTimeFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:sszzz";
}
// Date formatters don't grok a single trailing Z, so make it "GMT".
if ([dateTime hasSuffix: @"Z"]) {
dateTime = [[dateTime substringToIndex: dateTime.length - 1]
stringByAppendingString: @"GMT"];
}
NSDate *date = [xsdDateTimeFormatter dateFromString: dateTime];
if (!date) NSLog(@"could not parse date '%@'", dateTime);
return (date);
} // xsdDateTimeToNSDate
</pre><p>
<li> <b>Today's date</b> (NSDate->General)<br>
<pre>NSDate *today;
today = [NSDate date];</pre><p>
<li> <b>XCTest Macros</b> (Xcode->Tools)<br>
Quick list of Xcode unit testing assert macros.
<pre>
XCTAssertNil (expression, ...)
XCTAssertNotNil (expression, ...)
XCTAssert (expression, ...)
XCTAssertTrue (expression, ...)
XCTAssertFalse (expression, ...)
XCTAssertEqualObjects (expression1, expression2, ...)
XCTAssertNotEqualObjects (expression1, expression2, ...)
XCTAssertEqual (expression1, expression2, ...)
XCTAssertNotEqual (expression1, expression2, ...)
XCTAssertEqualWithAccuracy (expression1, expression2, accuracy, ...)
XCTAssertNotEqualWithAccuracy (expression1, expression2, accuracy, ...)
XCTAssertGreaterThan (expression1, expression2, ...)
XCTAssertGreaterThanOrEqual (expression1, expression2, ...)
XCTAssertLessThan (expression1, expression2, ...)
XCTAssertLessThanOrEqual (expression1, expression2, ...)
XCTAssertThrows (expression, ...)
XCTAssertThrowsSpecific (expression, exception_class, ...)
XCTAssertThrowsSpecificNamed (expression, exception_class, exception_name, ...)
XCTAssertNoThrow (expression, ...)
XCTAssertNoThrowSpecific (expression, exception_class, ...)
XCTAssertNoThrowSpecificNamed (expression, exception_class, exception_name, ...)
</pre><p>
<li> <b>Adding AppleHelp</b> (Xcode->General)<br>
<ol>
<li> Create a "AppName Help" directory in your <code>English.lproj</code>.<p>
<li> Add an <code>index.html</code> Put in these headers:<pre>
<head>
<meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
<title>AppName Help</title>
<meta name="AppleTitle" content="AppName Help">
<meta name="AppleIcon" content="AppName%20Help/images/icon-name.png">
<meta name="AppleFont" content="Lucida Grande,Helvetica,Arial">
<meta name="AppleSearchResultsFont" content="Lucida Grande,Geneva,Arial">
<meta name="robots" content="noindex">
</head>
</pre>
Along with your actual help content.<p>
<li> Drag the <code>AppName Help</code> directory into your XCode project. When Adding, make sure "Copy items into destination group's folder" is unchecked, and select "Create Folder References for any add folders". You wnat XCode to preserve your <code>AppName Help</code> directory.<p>
<li> Add these to your Info.plist:<pre>
<key>CFBundleHelpBookFolder</key>
<string>AppName Help</string>
<key>CFBundleHelpBookName</key>
<string>AppName Help</string>
</pre><p>
<li> Build, run, and cross your fingers. If Help.app is already running, you may want to exit it.
</ol><p>
<li> <b>Adding credits to your about box</b> (Xcode->General)<br>
If you have a file called "Credits.html" or "Credits.rtf", the contents of that file will be added to your about box. If your project doesn't have one already, you can use Xcode to add an empty file and put it in your English.lproj folder. Make sure you add it to your project.<p>
<li> <b>Apple-Generic Versioning</b> (Xcode->General)<br>
You can use agvtool do to apple-style generic versioning, which has two version numbers - one's a float that's monotonically increasing, and the other is a "marketing" version, that can be things like "1.0.4b34". Courtesy of Chris Hanson from a mailing list posting:
<p>
Set your Versioning System (VERSIONING_SYSTEM) to "apple-generic".
<p>
Set your Current Project Version (CURRENT_PROJECT_VERSION) to some floating point value that you'll increment every build. You could just start at 1, unless you already have builds out there with other numbers.
<p>
This will generate and build a source file automatically that defines two globals whenever you build your project. One is a double corresponding to CURRENT_PROJECT_VERSION, the other is a string. The file is a derived source file; it won't be added to your project, but it will be built with it.
<p>
If you're building a framework, there are other build settings you'll probably want to set as well, such as Current Version (DYLIB_CURRENT_VERSION), Compatibility Version (DYLIB_COMPATIBILITY_VERSION), and VERSION_INFO_PREFIX.
<p>
To update the version number, you can use agvtool next-version or agvtool new-version.<p>
<li> <b>Changing extension for a bundle</b> (Xcode->General)<br>
The default extension for a CocoaBundle is ".bundle". To change it to something else, use the WrapperExtension target property. Select the top-most item in the groups and files list, show the Info panel, choose Styles, and look for Wrapper Extension. Set that to what you want (such as "service"). Don't include the dot.<p>
<li> <b>Changing the default Project Builder projects</b> (Xcode->General)<br>
Don't like something about the default project that Xcode gives you? Look in <code>/Library/Application Support/Apple/Developer Tools/Project Templates/</code> and edit the projects there.
<p>
(For Project Builder, go to <code>/Developer/ProjectBuilder Extras/Project Templates/</code> and edit the projects there. The changes will take effect next time you create a new project.)<p>
<li> <b>Command-line building from xcode</b> (Xcode->General)<br>
<code>xcodebuild</code> is the <code>pbxbuild</code> equivalent. To get a development build, you'll need to specify the style:<br>
<code>% xcodebuild -buildstyle Development</code><p>
<li> <b>Debugging unit tests from the command-line</b> (Xcode->General)<br>
First, find the <code>.octest</code> file of interest:
<pre>% <b>find . -name "*.octest" -print</b>
./build/Debug/Tests.octest</pre>
Then gdb <code>Tools/otest</code>:
<pre>% <b>gdb /Developer/Tools/otest</b></pre>
Then run it with the name of the bundle:
<pre>(gdb) run <b>./build/Debug/Tests.octest</b></pre><p>
<li> <b>Enabling "control reaches end of non-void function"</b> (Xcode->General)<br>
One nice gcc warning that's not enabled by default is one that complains if you exit from a function (by falling off the end) without returning anything. This is an easy error to make, and can be hard to track down (especially if you do it in an objective-C init method). To enable this warning, add "-Wreturn-type" to the "Other C Flags" setting in your favorite build style.<p>
<li> <b>Fixing undefined symbols when building plugins</b> (Xcode->General)<br>
If you have a plugin defined as a "Cocoa Bundle" target in Xcode, and your plug-in inherits from a class that's defined in the application, you may get a build error like:<pre>
ld: Undefined symbols:
.objc_class_name_BWTrackerPlugin
</pre>
You can fix this by adding <code>-undefined dynamic_lookup</code> to the "Other Linker Flags" of the Build Tab of the Target Settings for your plugin (whew!). You also need to make sure that the "Mac OS X Deployment Target" is 10.2 or later before using this flag.<p>
<li> <b>Hiding TOC bar in xcode permanently</b> (Xcode->General)<br>
Thanks to Jens Bauer who sent this my way.
"When I option-double-click an object in my source-code, I got a surprise with newer Xcodes. There's a huge TOC bar in the left side, forcing me to have a large window, that overlaps my source-code, so I can't look at the documentation and type at the same time. Furthermore it makes the sentences very 'tall', which I can't get used to. So I dropped my coding, went for a hunt. I wanted to go and get rid of that annoying demon. I found it and cast it out (By hand!)"
<p>
Here's how Jens cast out that demon. It's two different edits:
<p>
To make the TOC section (left side bar) of the developer documentation default to be hidden, do as follows:
<p>
Before you begin, make the two files and their parent-folders writable.
<p>
In the file....
<p>
<code>/Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.CoreReference.docset/Contents/Resources/Documents/documentation/Resources/CSS/frameset_styles.css</code>
<p>
...change the line...
<p>
<code>#bodyText { margin-left: 210px; }</code>
<p>
...to read...
<p>
<code>#bodyText { /* margin-left: 210px; */ margin-left:10px; /* TOC-FIX */ }</code>
<p>
...And in the file...
<p>
<p>/Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.CoreReference.docset/Contents/Resources/Documents/documentation/Resources/JavaScript/page.js</p>
<p>
...add the following somewhere in initialize_page() function, for instance at the bottom, right before the closing brace...
<p>
<code> showHideTOC('hide'); // TOC-FIX</code>
<p>
...now you have a much better view!!
<p>
Note that you'll need to apply this patch when the docs get upgraded.<p>
<li> <b>How do I add a new folder to my Files view?</b> (Xcode->General)<br>
Select the folder you want the new folder to be a child of. Select <code>Project->New Group</code>. Give it a name. Note that the hierarchy of files in Xcode is a fantasy of the project, and doesn't necessarily reflect what's actually in the file system.<p>
<li> <b>IBOutlet collection syntax</b> (Xcode->General)<br>
<pre>@property (strong, nonatomic) IBOutletCollection(XXLaneView) NSArray *laneViews;</pre><p>
<li> <b>Moving archived apps from one machine to another</b> (Xcode->General)<br>
To move your archived apps from one machine to another (say you're upgrading to some shinier hardware or doing a nuke-and-pave install), you can find your old archived apps at:
<pre>~/Library/Developer/Xcode/Archives</pre><p>
<li> <b>Mysterious duplicated symbols</b> (Xcode->General)<br>
This killed a big chunk of time, so I figured I better record it here. While building a C++ shared library, I was getting errors like:
<p>
<code>ld: multiple definitions of symbol __ZN13CaptureWidgetD2Ev<br>
/Users/blah/blah/build/./capture.build/Debug/capture.build/Objects-normal/ppc/capturewidget-F17F43C0.o definition of __ZN13CaptureWidgetD2Ev in section (__TEXT,__text)<br>
/Users/blah/blah/build/./capture.build/Debug/capture.build/Objects-normal/ppc/capturewidget-F17F43C0.o definition of __ZN13CaptureWidgetD2Ev in section (__TEXT,__text)</code>
<p>
This is especially annoying, because the same object file is listed as having a duplicated symbol. If you take out the file from being compiled, you get undefined symbols for that particular source file, and if you put it back in, you get duplicated symbols.
<p>
Turn out the problem is that Xcode (somehow) added the C++ file to the "Build Sources" phase of the target. It happily generated two identical object files, and proceded to link them (hence the error), but doesn't get the reporting information correct (hence saying that both symbols come from the same file)<p>
<li> <b>Open documentation for a symbol</b> (Xcode->General)<br>
Say you have "NSFishmonger" in your code, and you want to see the header. Option-double-click on NSFishmonger and it will bring up the header file.<p>
<li> <b>Open header for a symbol</b> (Xcode->General)<br>
Say you have "NSColor" in your code, and you want to see the header. Command-double-click on NSColor and it will bring up the header file.<p>
<li> <b>Renaming "MyDocument"</b> (Xcode->General)<br>
I find the default name "MyDocument" that Xcode uses for new document-based Cocoa apps to be annoying, sounding more appropriate for Fisher-Price toys than professional software. Renaming all of the moving pieces in Xcode can be a bit daunting, and I don't want to edit the project template, since those tend to get revised as time goes on. This is what I do:
<ol>
<li> make the new project
<li> replace MyDocument with the newDocument name (BWGraphDocument here):<br>
<code>perl -pi -e 's/MyDocument/BWGraphDocument/g' *.[hm] *.xcodeproj/* *.plist</code>
<li>rename the document files:<br>
<code>
mv MyDocument.h BWGraphDocument.h<br>
mv MyDocument.m BWGraphDocument.m
</code>
<li> rename the nib file:<br>
<code>
mv MyDocument.nib BWGraphDocument.nib
<li> open the nib file, drag in your document header and change the class of the File's Owner to the new class. Delete the MyDocument type.
</code>
</ol><p>
<li> <b>Runing a unit test in ppc mode</b> (Xcode->General)<br>
Say your xcode project happily runs unit tests in i386 mode. You're about to land something that does byte-swapping, and you want to make sure it's sane in PPC mode, and you haven't turned on your G5 in months. You can do this command-line styles on your ICBM (assuming your project doesn't already do ppc and i386 unit tests). Build your test, and use <code>translate</code> to run the powerPC side of the world:
<pre>
% xcodebuild -configuration Release -target "Test All" -project Tests.xcodeproj NATIVE_ARCH="i386 ppc"
% /usr/libexec/oah/translate /Developer/Tools/otest build/Release/Tests.octest
</pre><p>
<li> <b>Running a specific unit test from the command line</b> (Xcode->General)<br>
<pre>% /Developer/Tools/otest -SenTest <i>ClassName</i> ./build/Debug/TheUnitTest.octest</pre><p>
<li> <b>Running a unit-test from the command line</b> (Xcode->General)<br>
<pre>% /Developer/Tools/otest <i>path/to/build/Unittest/TheUnit Test.octest</i></pre><p>
<li> <b>Setting compiler flags on a per-file basis</b> (Xcode->General)<br>
Sometimes you need to set compiler flags on a per-file basis, like you need to suppress a warning in one place or set a one-off #define, but not for the entire project. You can set these flags in the build tab of the Get Info window of the source file <b>in the target's <code>compile sources</code> build phase</b>. If you get info on the source file in other places in Xcode, there's no build tab, and you're left with that "Am I crazy? I know I've seen that setting somewhere before, but where is it? I better go back to listening to Finnish polkas 24/7 and give up this software thing" kind of feeling.<p>
<li> <b>Silence Xcode's beep on an unmatched brace.</b> (Xcode->General)<br>
Xcode likes to beep on an extra paren, brace, or bracket. That drives me nuts. You can turn it off via:
<pre>
% defaults write com.apple.dt.Xcode DVTTextBeepOnNonMatchingBrace -bool NO
</pre><p>
<li> <b>Toggle between the source file and its header file.</b> (Xcode->General)<br>
comand-option-up arrow<p>
<li> <b>Treat warnings as errors</b> (Xcode->General)<br>
In Xcode, there's a handy "Treat Warnings as Errors" checkbox. I usually use the search dealie with "werr" to find the setting.
<P>
(In Project Builder, open the Targets tab, double-click the target, go to the "Build Settings" tab, scroll down to Build Settings, look for <code>WARNING_CFLAGS</code>. Edit that, and add <code>-Werror</code> to what's already there.)<p>
<li> <b>Turning off Xcode's "Undo past save" warning</b> (Xcode->General)<br>
For some reason, Xcode thinks that an undo past the last save is something horrible that you need to be warned about. After living with unstable software, cmd-S is a habit, and undoing past it is No Big Deal. Really. Here's how to turn it off:
<pre>% defaults write com.apple.Xcode XCShowUndoPastSaveWarning NO</pre><p>
<li> <b>Turning off ZeroLink in all of the project templates</b> (Xcode->General)<br>
I loathe ZeroLink. It causes more problems than it fixes. The Xcode guys must love it dearly. The new .xcconfig files in Xcode 2.1 won't let you turn it off, rendering those files useless to me. The Official Apple suggestion is to edit all of your project templates (?). Here is a quick script that'll churn through all of the project templates (and also your own projects) and turn off ZeroLink.
<pre>
% cd '/Library/Application Support/Apple/Developer Tools/Project Templates'
% find . -name "*.pbxproj" -exec perl -pi -e "s/ZERO_LINK = YES/ZERO_LINK = NO/g" {} \; -print
</pre><p>
<li> <b>Using Objective C++</b> (Xcode->General)<br>
name your source file <code>blah.<b>M</b></code> (upper case <code>M</code>), or name your source file <code>blah.<b>mm</b></code> (two lower case <code>M</code>s). It's best to use <code>.mm</code> because of the case-insensitive (yet case-preserving) nature of HFS+<p>
<li> <b>XCode's default projects</b> (Xcode->General)<br>
XCode looks in <code>/Library/Application Support/Apple/Developer Tools/Project Templates/</code> for the templates for its projects. You can put your own project templates here, and tweak the existing templates (for instance, Xcode 1.1's Cocoa projects have a compiler warning in <code>main.m</code>, which is really annoying for those of use who treat warnings as errors. You can go into <code>/Library/Application Support/Apple/Developer Tools/Project Templates/Application/Cocoa Application/</code> and fix that warning if you wish.<p>
<li> <b>Blanket Disabling of ZeroLink</b> (Xcode->Hacks)<br>
(see also <a href="/miniblog/one-entry?entry%5fid=45678">ZeroLink Must Die</a>.
<p>
This <command>find</command> will grovel around project files and turn off ZeroLink. You can start at the path here to turn it off in all of your Xcode templates, and you can also point the find at your own XCode projects. Make backups first if you're feeling squeamish or your stuff isn't under version control.
<pre>
% cd '/Library/Application Support/Apple/Developer Tools/Project Templates'
% find . -name "*.pbxproj" -exec perl -pi -e "s/ZERO_LINK = YES/ZERO_LINK = NO/g" {} \; -print
</pre><p>
<li> <b>Changing __MyCompanyName__</b> (Xcode->Hacks)<br>
You've probably noticed that source files generated from within XCode include a comment block header:
<pre>//
// TapDance.h
// Groovilicous
//
// Created by markd on 7/25/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//</pre>
With the __MyCompanyName__ placeholder. There is no UI to change this, for obvious reasons. (Why would <b>anyone</b> want to easily and conveniently change something they'll otherwise need to edit in each and every source file they create. That's unpossible). The obvious solution is to drop to the terminal and run the straightforward command:
<pre>% defaults write com.apple.Xcode PBXCustomTemplateMacroDefinitions '{"ORGANIZATIONNAME" = "Borkware";}'</pre>
Seemple, no? Zee trick, she is doone.<p>
<li> <b>Could not open the application package</b> (Xcode->Hacks)<br>
If you get a mysterious Xcode hang, and then the alert "Could not open the application package", check the date and time on the device. For some reason iDevices eschew NTP and don't update their clocks, and can sometimes under conditions of battery drain revert to times in the past and stay there.<p>
<li> <b>Installing unix tools with Xcode 5</b> (Xcode->Hacks)<br>
Xcode 5 removed the unix tool download from the UI. It's also hidden fun things like <code>/usr/include</code>
<p>
Here's an easy way to fix this:
<p>
<code>% xcode-select --install</code>
<p>
This will kick off a download of the tools, and install them.<p>
<li> <b>Making "edit all in scope" work</b> (Xcode->Hacks)<br>
Xcode's "Edit All in Scope" feature is inexplicably tied to visible syntax colorization (actually, only Edit All in Scope for object pointers is tied to this, scalar variables and pointers work fine otherwise). So if you're not getting the Edit All in Scope menu item to enable, make sure "Color indexed Symbols" is turned on in the Xcode "Fonts and Colors" preferences.
<p>
This advice brought to you by a DTS incident.<p>
<li> <b>Quieting Xcode 8 log spew</b> (Xcode->Hacks)<br>
Xcode 8 logs an embarrassingly large amount of worthless information when running an app. (yes, multiple radars have been filed and subsequently duped into oblivion.)
<p>
You can work around it by setting this environment variable pair in your Run/Arguments scheme thing:
<pre>
OS_ACTIVITY_MODE=disable
</pre><p>
<li> <b>Suppressing static analyzer for a file</b> (Xcode->Hacks)<br>
Sometimes you have a crufty old file in your project that generates static analyzer warnings, but you're not in a place to fix them (like one of my old old projects has some zip/unzip code that causes compiler heartburn, but currently works). You can disable static analysis by passing these flags:
<pre>
-Xanalyzer -analyzer-disable-all-checks
</pre>
If you do this you should feel bad. I did this, and I feel bad.<p>
<li> <b>Turn debugging on with Xcode's indexer</b> (Xcode->Hacks)<br>
Got weird problems with Xcode's indexer? You can crank up its debugging log level.
<pre>
% defaults write com.apple.dt.xcode IDEIndexingClangInvocationLogLevel 3
</pre><p>
<li> <b>Turn off Xcode project navigator modifying the filesystem</b> (Xcode->Hacks)<br>
One of the worst misfeatures of Xcode9 is "hey, you moved stuff in the project navigator, I'm a gonna screw with your file system with no way to opt out!" This seems to turn it off for now:
<pre>
% defaults write com.apple.dt.Xcode IDEDisableStructureEditingCoordinator -bool YES
</pre><p>
<li> <b>Turn off xcode's brace-matching animation</b> (Xcode->Hacks)<br>
<pre>
% defaults write com.apple.dt.Xcode DVTTextShowMatchingBrace -bool NO
</pre>
I <3 Zach Waldowski for this tidbit.<p>
<li> <b>Non-chunky high-DPI image drawing</b> (NSImage->General)<br>
If you load NSImages from random files and draw them larger than they say they are, sometimes you get a really chunky, pixellated display for images that have a high DPI rather than a smooth interpolation (or even using all those extra pixels due to the high DPI). This is because (I think) that the image says that it's 100x100, even though you have 1000x1000 pixels available, and Cocoa decides to scale up only 100x100 pixels.
<p>
One way to hack around it is to tell the image to use the pixel size of the underlying image representation:
<pre> NSImage *image;
image = [[NSImage alloc] initWithContentsOfFile: path];
NSBitmapImageRep *rep = [[image representations] objectAtIndex: 0];
// If you think you might get something other than a bitmap image representation,
// check for it here.
NSSize size = NSMakeSize ([rep pixelsWide], [rep pixelsHigh]);
[image setSize: size];</pre><p>
<li> <b>Saving an image as a file</b> (NSImage->General)<br>
To save an imageRep as a PNG file:
<pre>
NSBitmapImageRep *bits = ...; // get a rep from your image, or grab from a view
NSData *data;
data = [bits representationUsingType: NSPNGFileType
properties: nil];
[data writeToFile: @"/path/to/wherever/test.png"
atomically: NO];
</pre>
There are also TIFF, BMP, GIF, JPEG file types in addition to PNG.<p>
<li> <b>Binding to your AppController</b> (Bindings->General)<br>
If you have an AppController object in your nib file, and it's not File's Owner, but you want to use bindings with it, you can use an NSObjectController :
<ol>
<li> Make an IBOutlet in your controller to point to the NSObjectController
<li> Drag an NSObjectController into the nib
<li> Add the names of the properties you're going to be binding with
<lI> in AppController's <code>awakeFromNib</code> method, do <code>[objController setContent: self];</code>
</ol>
By setting the content value in <code>awakeFromNib</code> helps prevent a retain cycle which can lead to memory leaks.<p>
<li> <b>Changing the "Not Applicable" text in tableviews</b> (Bindings->General)<br>
Sometimes you want to bind a tableview column to objects that might not support that particular binding (like having an uber-overview tableview for core data entities that don't all share the same attributes). If your column has a numeric formatter, IB won't let you specify a string for the "not applicable" value, so you end up with a really ugly tableview covered in "Not Applicable"s
<p>
Put this in a convenient place:
<pre>+ (void) initialize
{
[NSTextFieldCell setDefaultPlaceholder: @""
forMarker: NSNotApplicableMarker
withBinding: NSValueBinding];
} // initialize</pre><p>
<li> <b>Handling KVO with arrays</b> (Bindings->General)<br>
When you insert or remove objects in a KVO compliant manner, your observer is informed of the specifics of the change via the change dictionary:
<pre>- (void) observeValueForKeyPath: (NSString *) keyPath
ofObject: (id) object
change: (NSDictionary *) change
context: (void *) context</pre>
The <code>NSKeyValueChangeKindKey</code> key in the dictionary tells you if the change was an insertion
(<code>NSKeyValueMinusSetMutation</code>) or a deletion (<code>NSKeyValueIntersectSetMutation</code>)
<p>
If it is an insertion, the <code>NSKeyValueChangeIndexesKey</code> is an index set that contains the index of the inserted object. You can query the collection for the object at that index to get the new object.
<p>
if it a deletion, the <code>NSKeyValueChangeIndexesKey</code> tells you the index where the object was deleted from, and the <code>NSKeyValueChangeOldKey</code> contains an NSArray of objects which were removed, in case you want to hang on to it, or use it to clean out some of your data structures.<p>
<li> <b>Handling unknown keys with KVC</b> (Bindings->General)<br>
Override <code>valueForUndefinedKey:</code>:
<pre>- (id) valueForUndefinedKey: (NSString *) key
{
id value;
value = [self lookUpValueUsingSomeOtherMechanism: key];
if (value == nil) {
value = [super valueForUndefinedKey: key];
}
return (value);
} // valueForUndefinedKey</pre>
Some handy uses for this is using the user defaults for storing values (you can use the key directly to <code>[NSUserDefaults stringForKey:]</code>, or use it to query the contents of an <code>NSDictionary</code>
<p>
The counterpart for this is
<p>
<code>- (void) setValue: (id) value forUndefinedKey: (NSString *) key</code>, which you can use to stash stuff into user prefs or a dictionary.<p>
<li> <b>Hooking up a search box to your array controller</b> (Bindings->General)<br>
Tiger supposedly does this for us. If you're having to support Panther as well, here's a way to have a search box filter the contents managed by an NSArrayController.
<p>
In the header file<pre>#import <Cocoa/Cocoa.h>
@interface BWSearchArrayController : NSArrayController
{
NSString *searchString;
}
- (IBAction) search: (id) sender;
@end // BWSearchArrayController</pre>
and then in the implementation:<pre>// returns an array containing the content of the objects arranged
// with the user's critera entered into the search box thingie
- (NSArray *) arrangeObjects: (NSArray *) objects
{
// result of the filtering
NSArray *returnObjects = objects;
// if there is a search string, use it to compare with the
// search field string
if (searchString != nil) {
// where to store the filtered
NSMutableArray *filteredObjects;
filteredObjects = [NSMutableArray arrayWithCapacity: [objects count]];
// walk the enumerator
NSEnumerator *enumerator = [objects objectEnumerator];
id item; // actully BWFileEntries
while (item = [enumerator nextObject]) {
// get the filename from the entry
NSString *filename;
filename = [item valueForKeyPath: @"fileName"];
// see if the file name matches the search string
NSRange range;
range = [filename rangeOfString: searchString
options: NSCaseInsensitiveSearch];
// found the search string in the file name, add it to
// the result set
if (range.location != NSNotFound) {
[filteredObjects addObject: item];
}
}
returnObjects = filteredObjects;
}
// have the superclass arrange them too, to pick up NSTableView sorting
return ([super arrangeObjects:returnObjects]);
} // arrangeObjects</pre>
and then to set the search string: <pre>- (void) setSearchString: (NSString *) string
{
[searchString release];
if ([string length] == 0) {
searchString = nil;
} else {
searchString = [string copy];
}
} // setSearchString
- (void) search: (id) sender
{
[self setSearchString: [sender stringValue]];
[self rearrangeObjects];
} // search
</pre><p>
<li> <b>KVO Array controller gotcha</b> (Bindings->General)<br>
If you're doing KVO and don't override <code>observeValueForKeyPath:ofObject:change:context:</code>, (or you do override it but don't handle your callbacks) you'll generally get an exception.
This trains you to do it right and never call super for the callbacks you're handling, or you'll get an exception.
<p>
<b>Except</b>
if you've added KVO to a subclass of <code>NSArrayController</code> (and possibly all controller types), and don't call super's <code>observeValueForKeyPath:ofObject:change:context:</code>, bindings won't work at all, with no warning/notice/nothing. (Courtesy of Duncan Wilcox)<p>
<li> <b>Manually creating a binding</b> (Bindings->General)<br>
<pre>[imageView bind: @"valuePath"
toObject: imagesController
withKeyPath: @"selection.fullPath"
options: nil];</pre>
In Interface Builder, "Bind To" corresponds to <code>imagesController</code>, "Controller Key" would be <code>selection</code>, and "Model Key Path would be <code>fullPath</code>.
<p>
Use <pre> [imageView unbind: @"valuePath"];</pre> to remove a binding.<p>
<li> <b>Minimal KVC accessors for arrays</b> (Bindings->General)<br>
For an attribute named "layers", here are the KVC accessors to write to let observers react to object addition and removal:
<pre>- (NSArray *) layers
{
return (layers);
} // layers
- (void) insertObject: (id) obj
inLayersAtIndex: (unsigned) index
{
[layers insertObject: obj atIndex: index];
} // insertObjectInLayers
- (void) removeObjectFromLayersAtIndex: (unsigned) index
{
[layers removeObjectAtIndex: index];
} // removeObjectFromLayersAtIndex</pre><p>
<li> <b>Monitoring one array from two controllers</b> (Bindings->General)<br>
If you're wanting to use two NSArrayControllers to monitor one NSMutableArray, you'll need to use each of the controller's contentArray binding. Just using the content outlet (or setContent: call), you'll only get arrangedObjects updates from one controller. (thanks to Duncan Wilcox for this one)<p>
<li> <b>NSSearchField filtering for an NSArrayController</b> (Bindings->General)<br>
In Tigger, NSSearchField can now automagically filter an array via IB magic.
<p>
In Interface Builder, drag over the NSSearchField, and bind the <code>predicate</code> like this:
<ul>
<li> Bind To: your array controller
<li> Controller Key: filterPredicate
<li> Predicate Format: <i><code>what contains[c] $value</code></i> (assuming the attribute you're filtering is called <code>what</code>)
</ul>
If you're wanting to be able to filter on multiple attributes, make additional predicate bindings. Doing the CoreData option-drag of an entity into IB is really handy - take a peek at how it configures the search field for filtering multiple columns.<p>
<li> <b>Observing using KVO</b> (Bindings->General)<br>
Register an observer with something like:
<pre> [searchArrayController addObserver: self
forKeyPath: @"selectionIndexes"
options: NSKeyValueObservingOptionNew
context: NULL];</pre>
This makes <code>self</code> an observer of the <code>searchArrayController</code>. We'll get notified when the <code>selectionIndexes</code> value changes, and we'll be notified with the new value.
<p>
When the notification happens, this method (invoked against the <code>self</code> used earlier) is invoked:
<pre>- (void) observeValueForKeyPath: (NSString *) keyPath
ofObject: (id) object
change: (NSDictionary *) change
context: (void *) context
{
} // observeValueForKeyPath
</pre>
and you can poke around the arguments to see wha'happened.<p>
<li> <b>Registering default user defaults</b> (Bindings->General)<br>
Here is one way to register the "factory settings" for your user defaults (preferences / configurations). You will pick up these values automagically via bindings to the Shared Defaults.
<pre>- (void) setDefaultPrefs
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject: NSLocalizedString(@"BorkDown", nil)
forKey: @"menuTitle"];
[dictionary setObject: NSLocalizedString(@"Time has run out.", nil)
forKey: @"alertWindowText"];
// and any other preferences you might have
[[NSUserDefaults standardUserDefaults]
registerDefaults: dictionary];
} // setDefaultPrefs</pre><p>
<li> <b>Using KVO for whole-object observation</b> (Bindings->General)<br>
I've got an object that has a bunch of individual attributes that are controlled by an inspector, and another object (a view that holds that object) which needs to redraw when something changes in the object, but it doesn't care which individual attribute it is. Rather than having the view observe each of the individual attributes, KVO provides a way to automatically trigger another observation when any dependent attribute changes.
<p>
First, in the +initialize for the class that's going to be observed:
<pre>+ (void) initialize
{
NSArray *keys;
keys = [NSArray arrayWithObjects: @"showMajorLines", @"minorWeight",
@"minorColor", @"minorWeightIndex", @"minorColorIndex",
@"majorWeightIndex", @"majorColorIndex", nil];
[BWGridAttributes
setKeys: keys
triggerChangeNotificationsForDependentKey: @"gridAttributeChange"];
} // initialize</pre>
So now when "showMajorLines" changes, "gridAttributeChange" will also be observed. KVO requires there actually must exist a gridAttributeChange method (or ivar I presume) before it'll do the notification to observing objects, so there needs to be a do-nothing method:
<pre>- (BOOL) gridAttributeChange
{
return (YES);
} // gridAttributeChange</pre>
So now the view can do this:
<pre>- (void) setGridAttributes: (BWGridAttributes *) a
{
[attributes removeObserver: self
forKeyPath: @"gridAttributeChange"];
[a retain];
[attributes release];
attributes = a;
[a addObserver: self
forKeyPath: @"gridAttributeChange"
options: NSKeyValueObservingOptionNew
context: NULL];
} // setGridAttributes</pre>
And will get updated whenever an individual attribute changes.<p>
<li> <b>View with bindings</b> (Bindings->General)<br>
<a href="/products/thumbborker/">ThumbBorker</a> has a custom view class that
updates a selection rectangle when the user finished dragging the
mouse. It uses <code>setValue:forKeyPath:</code> to stick the value back into the
bound object.
<p>
The bindings and path are ivars:
<pre> id selectionRectBinding;
NSString *selectionRectKeyPath;</pre>
In <code>bind:toObject:withKeyPath:options:</code> hang on to the binding
object and the key path and set up observing:
<pre> // hold on to the binding info
selectionRectBinding = observableObject;
selectionRectKeyPath = [observableKeyPath copy];
// connect KVO
[valuePathBinding addObserver: self
forKeyPath: selectionRectKeyPath
options: nil
context: NULL];
// new binding, needs to redraw
[self setNeedsDisplay: YES];</pre>
And in the mouseUp: handler, set the value back into the bound object:
<pre> // figure out the selection rectangle
NSRect selectionRect = [self normalizedSelectionRect];
// wrap in a value and tell the bound object the new value
NSValue *value;
value = [NSValue valueWithRect: selectionRect];
[selectionRectBinding setValue: value
forKeyPath: selectionRectKeyPath];</pre><p>
<li> <b>Fixing Bindings-related memory leak</b> (Bindings->Hacks)<br>
If you have bindings hooked up to File's Owner in some circumstances (like with an NSWindowController-based nib file), a retain cycle is created, and so the window controller will never get released (having an extra retain for each binding to File's Owner). An easy way to work around this is to;
<ul>
<li> Add an NSObjectController (I call it "Fake FIle's Owner") to the nib file and the window controller class.
<li> Point the bindings to the controller rather than file's owner
<li> Hook up the window controller's outlet to point to the object controller
<li> [fakeFileOwner setContent: self]
</ul>
Then again, this all might be Wrong. jkp_ in #macdev points out that the setContent:self will retain self twice more, with no release in sight. The only wayd that would really work is if you provided a subclass of NSObjectController that released after it setContent: and also in _startObservingObject: That might be the cleanest solution - provide a custom proxy object that doesnt retain the filesOwner and bind to that....same thing, only you have to do the overrides. So this one's in limbo(e) for now. wheeee! He's come up with a hack to work around things: <pre>- (oneway void) release;
{
// special case when count is 3, we are being retained twice by the object controller...
if ( [self retainCount] == 3 )
{
[super release];
[filesOwnerProxy setContent:nil];
return;
}
[super release];
}</pre><p>
<li> <b>Enabling buttons based on tableview selection</b> (Bindings->NSTableView)<br>
Bind to your array controller <code>selection</code>, using <code>@count</code>.<p>
<li> <b>Reacting to TableView selection changes when using bindings</b> (Bindings->NSTableView)<br>
You're using bindings to handle the tableview, but now you have some code that needs to do something when the selection changes (in my case I was disabling a sheet's OK button if the selection in two tableviews was the same). The way to track that change is to add yourself as an observer of the array controller that's driving the tableview, and then do the work in the <code>observeValueForKeyPath:</code> method. Register your observer like this:
<pre> [searchArrayController addObserver: self
forKeyPath: @"selectionIndexes"
options: NSKeyValueObservingOptionNew
context: NULL];</pre>
So now self's observeValue method will get invoked when <code>selectionIndexes</code> changes in the array controller.<p>
<li> <b>Responding to a scrollview scrolling</b> (NSScrollView->General)<br>
If you need to react to a scrollview being scrolled by the user, first tell the contentView (the NSClipView) to post notifications when its bounds changes, and then register to receive an NSViewBoundsDidChangeNotification:
<pre> [[scrollView contentView] setPostsBoundsChangedNotifications: YES];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter] ;
[center addObserver: self
selector: @selector(boundsDidChangeNotification:)
name: NSViewBoundsDidChangeNotification
object: [scrollView contentView]];
...
- (void) boundsDidChangeNotification: (NSNotification *) notification
{
[self setNeedsDisplay: YES];
// or whatever work you need to do
} // boundsDidChangeNotification
</pre><p>
<li> <b>Scrolling an NSScrollView programatically</b> (NSScrollView->General)<br>
Sometimes you need to scroll a scrollview outside of user control. You first have to tell the content view (Which is an NSClipView) to scroll to a point, and then tell the scrollview to adjust its scrollbar position:
<pre> [[scrollView contentView] scrollToPoint: pointToScrollTo];
[scrollView reflectScrolledClipView: [scrollView contentView]];
</pre><p>
<li> <b>Add new directory directly to repository</b> (Subversion->General)<br>
<code>% svn mkdir -m "initial revision" file:///usr/local/svnroot/MilleBorks</code><p>
<li> <b>Adding new project to repository (1 project : 1 repository)</b> (Subversion->General)<br>
The Subversion folks recommend that project have a branches, tag, and trunk directory, to make doing things like branches and tags easier. Importing this way will mean your repository will only have this one project.
<pre>% mkdir DungeonBorkventure
% cd DungeonBorkventure
% mkdir branches tags trunk
# put source files into trunk
% svn import . file:///usr/local/svnroot -m "initial checkin"
% cd ..
% rm -rf DungeonBorkventure # or move it aside
% svn checkout file:///usr/local/svnroot/trunk DungeonBorkventure
% cd DungeonBorkventure
# and get to work</pre><p>
<li> <b>Adding new project to repository (multiple projects : 1 repository)</b> (Subversion->General)<br>
To have multiple projects in one repository, you have to create directories in the repository first to hold the project, then import the code. Here is importing two projects, MilleBorks and DungeonBorkventure
<p>
First, make the directories in the repository:
<pre>% svn mkdir -m "initial revision" file:///usr/local/svnroot/DungeonBorkventure
% svn mkdir -m "initial revision" file:///usr/local/svnroot/MilleBorks</pre>
Then import the different code bases:
<pre>% cd /path/to/DungeonBorkventure
% svn import -m "initial revision" . file:///usr/local/svnroot/DungeonBorkventure
% cd /path/to/MilleBorks
% svn import -m "initial revision" . file:///usr/local/svnroot/MilleBorks</pre>
Then checkout working copies of the projects:
<pre>
% cd /work/and/play/area
% svn checkout file:///usr/local/svnroot/MilleBorks/trunk MilleBorks
% svn checkout file:///usr/local/svnroot/DungeonBorkventure/trunk DungeonBorkventure
</pre><p>
<li> <b>Ignoring files</b> (Subversion->General)<br>
I don't want subversion to mess with some files, or to tell me about them when doing an <code>svn status</code>. Add this to your <code>~/.subversion/config</code> file.
<pre>
[miscellany]
global-ignores = build *.mode* *.pbxuser *~.nib .DS_Store *~
</pre>
Now it won't bug me about the build directory the -per-user Xcode files, nib backup files, the #$&!! .DS_Store file that the Finder litters everywhere, and also don't bug me about emacs backup files.
<p>
You can search for <code>global-ignores</code> in that file to see some info about the setting. You might want to check out <a href="http://riquedafreak.blogspot.com/2007/12/spring-cleaning-with-subversion.html">Spring Cleaning with Subversion</a> too.<p>
<li> <b>Make archive of locally modified files in subversion</b> (Subversion->General)<br>
<pre>
$ zip ~/modified.zip $(svn status | grep ^M | awk '{ print $2;}')
</pre>
(Bash syntax)<p>
<li> <b>Making a new FSFS repository</b> (Subversion->General)<br>
<code>% svnadmin create --fs-type fsfs /usr/local/svnroot/</code><p>
<li> <b>Making a tag / branch</b> (Subversion->General)<br>
This will make a tag called "stable-1" of the current trunk revision:
<pre>
% svn copy file:///usr/local/svnroot/HackDie/trunk
file:///usr/local/svnroot/HackDie/tags/stable-1
-m "tag for HackDie internal stable release #1"
</pre><p>
<li> <b>Seeing what files will come down in the next update</b> (Subversion->General)<br>
To see if folks have committed changes that'll get brought down in the next update.
<p>
<code>% svn status --show-updates</code>
<p>
You can specify files and directories of interest.<p>
<li> <b>Showing all files that changed at a particular revision</b> (Subversion->General)<br>
If you're inside of a working copy, you can do:
<pre>% <b>svn log -v -r 373</b>
------------------------------------------------------------------------
r373 | markd | 2005-06-22 16:05:38 -0400 (Wed, 22 Jun 2005) | 1 line
Changed paths:
A /xyz/trunk/docs/bugreports/4158233-nsbrowser
A /xyz/trunk/docs/bugreports/4158233-nsbrowser/browser-rows.png
A /xyz/trunk/docs/bugreports/4158233-nsbrowser/browserdraw.tar.gz
A /xyz/trunk/docs/bugreports/4158233-nsbrowser/bug.txt
initial revision
------------------------------------------------------------------------
</pre>
and you can look repository-wide too:
<code>% svn log -v -r 373 file:///usr/local/svnroot/</code><p>
<li> <b>Subversion over ssh</b> (Subversion->General)<br>
To access a repository that lives over ssh (vs http or file), use the svn+ssh protocol thingie in the commands, like<br>
<code>% svn mkdir -m "initial revision" svn+ssh://borkware.com/path/to/svnroot/thingie</code><p>
<li> <b>Adding blah@2x.png files to subversion</b> (Subversion->Hacks)<br>
I personally dislike Apple's "@2x" nomenclature for high-def images for iDevices. But we're stuck with it. The embedded at-sign confuses subversion unless you append the file name with an at-sign:
<pre>
% svn add bunnies@2x.png
svn: warning: 'bunnies' not found
% svn add bunnies@2x.png@
A bunnies@2x.png
</pre>
(thanks to Gus Mueller, Mike Ash, and Guy English)<p>
<li> <b>Changing a working tree's repository location</b> (Subversion->Hacks)<br>
Sometimes your subversion repository moves (like if you're using a private one and the machine changes addresses). Here's how to repoint a working tree to the new location (handy if there's outstanding changes that are a pain to move over to a fresh checkout)
<pre>% svn switch --relocate http://from-address.flongswozzle.net http://to-address.borkware.com</pre>
Muchos thankos to Lucas Eckels for the tip.<p>
<li> <b>Changing the text for a commit</b> (Subversion->Hacks)<br>
Occasionally, when I'm checking in a bunch of files individually (say each file has a distinct change), I might accidentally do something dumb like a svn commit without a filename, and suddenly everything's checked in. Sigh. So now the commit message is for a particular one-file change, and now the rest of the files have this comment. Sigh again.
<p>
If you're in control of the repository, you can put the proper log message into a text file, and hack your repository (assuming oop.txt has the new comment contents, and it was revision 285 that accidentally got All The Things):
<pre>
% cd parent-directory-of-repository
% svnadmin --bypass-hooks setlog ./svnroot -r 285 /tmp/oop.txt
</pre>
If your repository is more than "just your own thing", you might want to follow the more complicated instructions at at the <a href="http://subversion.apache.org/faq.html#change-log-msg">Subversion FAQ</a>.<p>
<li> <b>Custom Accessor Methods (scalar)</b> (Core Data->General)<br>
Say there's a float ivar called <code>radius</code>
<pre>
- (float) radius
{
[self willAccessValueForKey: @"radius"];
float f = radius;
[self didAccessvalueForKey: @"radius"];
return (f);
} // radius
- (void) setRadius: (float) newRadius
{
[self willChangeValueForKey: @"radius"];
radius = newRadius;
[self didChangeValueForKey: @"radius"];
} // setRadius
</pre><p>
<li> <b>Custom accessor methods (objects)</b> (Core Data->General)<br>
Wrap the methods with will/did Access/Change ValueForKey:, and also use KVC to set primitive values:
<pre>
- (NSString *) name
{
[self willAccessValueForKey: @"name"];
NSString *string = [self primitiveValueForKey: @"name"];
[self didAccessValueForKey: @"name"];
} // name
- (void) setName: (NSString *) x
{
[self willChangeValueForKey: @"name"];
[self setPrimitiveValue: x forKey: @"name"];
[self didChangeValueForKey: @"name"];
} // setName
</pre><p>
<li> <b>Inserting an object</b> (Core Data->General)<br>
Inserting a new object into a managed object context is a multi-stage process. This assumes that <code>self</code> has methods to get the managed object context and managed object model (Apple's CoreData Application template does)
<p>
Thanks to Evan Moseman who pointed me at the new easy way to do it:
<pre>
NSManagedObjectContext *moc = [self managedObjectContext];
NSManagedObject *obj = [NSEntityDescription
insertNewObjectForEntityForName :@"Condition"
inManagedObjectContext: context];
</pre>
And the older, more manual way if you need some more control over the process:
<pre>
NSManagedObjectContext *moc = [self managedObjectContext];
NSManagedObjectModel *mom = [self managedObjectModel];
NSEntityDescription *entity;
entity = [[mom entitiesByName] objectForKey: @"Condition"];
NSString *className;
className = [entity managedObjectClassName];
NSManagedObject *obj;
obj = [[NSClassFromString(className) alloc]
initWithEntity: entity
insertIntoManagedObjectContext: moc];
[obj autorelease];
[obj setValue: @"nook" forKey: @"name"];
[obj setValue: [NSNumber numberWithInt: 23] forKey: @"ranking"];
</pre><p>
<li> <b>Manipulating a to-many relationship</b> (Core Data->General)<br>
Use <code>mutableSetValueForKey:</code> This returns a proxy that mutates the relationship and does KVO notifications. Think of the name as "[NS]MutableSet" "valueForKey" rather than "mutableSetValue" "forKey", because it returns a mutable set that you manipulate
<pre>NSMutableSet *employees;
employees = [department mutableSetValueForKey: @"employees"];
[employees addObject: newEmployee];
[employees removeObject: sackedEmployee];
</pre><p>
<li> <b>Manually sorting results in a fetch request</b> (Core Data->General)<br>
I haven't figured out how to get the Xcode fetched query editor to sort. In the mean time, here's how to do it in code:
<pre>
NSSortDescriptor *sorter;
sorter = [[NSSortDescriptor alloc]
initWithKey: @"when"
ascending: YES];
[fetchRequest setSortDescriptors:
[NSArray arrayWithObject: sortDescriptor]];
</pre><p>
<li> <b>Setting computed default value for a managed object</b> (Core Data->General)<br>
Make a subclass of NSManagedObject and override <code>awakeFromInsert</code>:
<pre>- (void) awakeFromInsert
{
[super awakeFromInsert];
NSDate *date = [NSDate date];
[self setValue: date forKey: @"when"];
} // awakeFromInsert</pre><p>
<li> <b>Sorting results</b> (Core Data->General)<br>
Want data to be sorted in tableviews and whatnot? You can set the "sortDescriptors" binding on an array controller. Write a method that returns the sort descriptors you want:
<pre>
- (void) setWhenSortDescriptors: (NSArray *) descriptors
{
} // setWhenSortDescriptors
- (NSArray *) whenSortDescriptors
{
NSSortDescriptor *sorter;
sorter = [[[NSSortDescriptor alloc]
initWithKey: @"when"
ascending: NO] autorelease];
return ([NSArray arrayWithObject: sorter]);
} // whenSortDescriptors
</pre>
This is for a 'permanent' sorting, not allowing the user to change the sorting. Also, new/changed objects added to the collection don't appear to be placed in sorted order.<p>
<li> <b>Storing Images into the data store</b> (Core Data->General)<br>
So ya want to store an image into the data store. mmalc says
<p>
Don't use the XML store, but instead use the SQLite store
<p>
If an entity has a large data blob as an attribute, you should make a separate entity for that attribute.
<p>
e.g. a Person has a 'photo' attribute. Create a Photo entity with a single attribute (the data) and a relationship back to the person (if it makes sense, usually it does), and a relationship from the Person to the Photo entity.
<p>
This means that the photo data will only be loaded from the persistent store if you actually use it.
<p>
(markd: this also has the nice side effect of letting the db put all the blob data elsewhere, rather than in with the other data, giving you better locality of reference to your Person data without slogging through all the Photo jazz if you don't need it)<p>
<li> <b>Unslecting all segments with radio-mode tracking</b> (NSSegmentedControl->Hacks)<br>
NSSegmentedControl won't let you unselect all segments if there is currently one segment selected (it's perfectly happy with having everything unselected so long as nothing else is selected). This is annoying, so you have to go into the Momentary tracking mode, unselect each of the cells, then go back to the original mode.
<p>
In my NSSegmentedControl category, I have a method to solve this problem:
<pre>@interface NSSegmentedControl (BorkwareAdditions)
- (void) unselectAllSegments;
// ... other goodies
@end // NSSegmentedControl
- (void) unselectAllSegments
{
NSSegmentSwitchTracking current;
current = [self trackingMode];
[self setTrackingMode: NSSegmentSwitchTrackingMomentary];
int i;
for (i = 0; i < [self segmentCount]; i++) {
[self setSelected: NO forSegment: i];
}
[self setTrackingMode: current];
} // unselectAllSegments</pre><p>
<li> <b>Enabling the Safari debug menu</b> (Safari->General)<br>
<code>defaults write com.apple.Safari IncludeDebugMenu 1</code><p>
<li> <b>console logging in Safari</b> (Safari->General)<br>
<pre> if (window.console) {
window.console.log ("ook1");
}
</pre>
(<code>window.console</code> exists in Safari, but not in Dashboard)<p>
<li> <b>Displaying archived web data in a WebView</b> (WebKit->General)<br>
<pre>WebArchive *archive = ... get from somewhere...;
WebFrame *frame = [webView mainFrame];
[frame loadArchive: webarchive];</pre><p>
<li> <b>Opening webview links in the user's default browser.</b> (WebKit->General)<br>
<pre>
...
[someWebView setPolicyDelegate: self];
...
- (void) webView:(WebView *)webView
decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
frame:(WebFrame *)frame
decisionListener:(id<WebPolicyDecisionListener>)listener {
NSURL *url = [request URL];
if (url != nil) {
[[NSWorkspace sharedWorkspace] openURL:url];
}
} // decidePolicyForNavigationAction
</pre><p>
<li> <b>Preventing Safari from displaying PDFs</b> (WebKit->General)<br>
I loathe using Safari to read PDFs. To tell Safari to stop it, try doing
<pre>% defaults write com.apple.Safari WebKitOmitPDFSupport -bool YES</pre>
if you have the misfortune of having the Acrobat Reader plug-in, you can nuke <code>/Library/Internet Plug-Ins/AdobePDFViewer.plugin</code><p>
<li> <b>Caveman debugging in both Safari and Dashboard</b> (Dashboard->General)<br>
It'd be too convenient to have a uniform mechanism to perform caveman debugging of widgets in both Safari-preview mode (where you can use window.console.log) and Dashboard mode (where you can't). Luckily (?), window.alert in Dashboard will log to the console in Dashboard, but shows an alert in safari.
<p>
So, combine the two for maximal pleasure:
<pre>function cavemanLog(message) {
if (window.widget) {
// Won't display an error box, but will show in the console log.
window.alert(message);
} else {
// Send output to the javascript console.
window.console.log(message);
}
} // cavemanLog</pre><p>
<li> <b>Runing a widget without installing it</b> (Dashboard->General)<br>
When you double-click a widget or <code>open</code> it from the terminal, you get asked if you want to install it (which will move your widget to <code>~/Library/Widgets</code>, which is really annoying when it drags it out of your development area. When faced with the dialog, hold down command and option to get a "Run" button. That'll run the widget in-pace. If you tun on the devmode:
<pre>% defaults write com.apple.dashboard devmode YES</pre>
You can drag widgets out of the dashboard area and have them on-screen. Cmd-R will reload the widget.<p>
<li> <b>Creating scripting bridge header file for an app</b> (AppleScript->General)<br>
<pre>
% sdef /Applications/iTunes.app | sdp -fh --basename iTunes
</pre>
Makes iTunes.h<p>
<li> <b>Running a pre-canned applescript</b> (AppleScript->General)<br>
Here's a way to run a 'fire and forget' Applescript that lives in your application bundle.
<pre>#define runScriptName @"checknewnow"
#define runScriptType @"scpt"
- (IBAction)runScript:(id)sender
{
/* Locate that darn thing*/
NSString *scriptPath = [[NSBundle mainBundle]
pathForResource: runScriptName
ofType: runScriptType];
NSURL *scriptURL = [NSURL fileURLWithPath: scriptPath];
NSAppleScript *as = [[NSAppleScript alloc]
initWithContentsOfURL: scriptURL
error: nil];
[as executeAndReturnError: NULL];
[as release];
}</pre>
(Thanks to of Chris "The_Tick" Forsythe)<p>
<li> <b>Format of new pages</b> (VoodooPad->General)<br>
Make a page called NewPageTemplate. Use <code>$title$</code> for the page term.<p>
<li> <b>Unladen Swallows?</b> (VoodooPad->AppleScript)<br>
<pre>tell application "VoodooPad"
taunt
end tell</pre><p>
<li> <b>Putting an NSXMLDocument into a text view</b> (NSXML->General)<br>
<pre>
NSData *blah = [xmldocument XMLDataWithOptions: NSXMLNodePrettyPrint];
NSString *snork = [[NSString alloc] initWithData: blah
encoding: NSUTF8StringEncoding];
NSAttributedString *arrrgh;
arrrgh = [[NSAttributedString alloc] initWithString: snork];
[[xmlTextView textStorage]
setAttributedString: [arrrgh autorelease]];
</pre><p>
<li> <b>Reverting all files</b> (Perforce->General)<br>
<pre>% p4 revert -c default ...</pre><p>
<li> <b>Reverting opened, but unedited files</b> (Perforce->General)<br>
<pre>
p4 revert -a
</pre>
will revert all files that haven't actually be edited, leaving the edited opened.
<p>
(Thanks to TVL for this one)<p>
<li> <b>Turning off the executable bit in Perforce</b> (Perforce->General)<br>
<pre>
p4 edit -t text [file_list]
p4 submit
</pre>
(Thanks to TVL for this one)<p>
<li> <b>Cad Macro</b> (ClanLord->Random)<br>
From Seilk. I can never find it when I want it.
<pre>
shift-click
{
$any_click
if @click.simple_name == ""
if @my.right_item == "caduceus"
"\use /off\r"
end if
else if @click.simple_name == @my.simple_name
if @my.right_item != "moonstone"
"\equip \"moonstone\"\r"
end if
"\use 3\r"
else
if @my.right_item != "caduceus"
"\equip \"caduceus\"\r"
end if
"\use " @click.simple_name "\r"
message "* Now healing" @click.name
end if
}
</pre><p>
<li> <b>Binary Searching in a sorted NSArray</b> (NSArray->General)<br>
How do you binary search a sorted NSArray? Use toll free bridging to CFArray which actually has a binary search function:
<pre>
NSMutableArray *sortedArray = [NSMutableArray arrayWithObjects: @"Alice", @"Beth", @"Carol",@"Ellen",nil];
//Where is "Beth"?
unsigned index = (unsigned)CFArrayBSearchValues((CFArrayRef)sortedArray,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)sortedArray)),
(CFStringRef)@"Beth",
(CFComparatorFunction)CFStringCompare,
NULL);
if (index < [sortedArray count] && [@"Beth" isEqualToString:[sortedArray[index]])
{
NSLog(@"Beth was found at index %u", index);
} else {
NSLog(@"Beth was not found (index is beyond the bounds of sortedArray)");
}
//Where should we insert "Debra"?
unsigned insertIndex = (unsigned)CFArrayBSearchValues((CFArrayRef)sortedArray,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)sortedArray)),
(CFStringRef)@"Debra",
(CFComparatorFunction)CFStringCompare,
NULL);
[sortedArray insertObject:@"Debra" atIndex:insertIndex];
NSLog([sortedArray description]);
//note: NSArray indices and counts were typed as unsigned. With the move to 64-bit, they are NSUInteger.
//CFArray indices and counts are CFIndex, which was SInt32 but also will move to 64-bit?
//Why was it ever signed and will it remain so?
</pre>
Muchos Thankos to James Hober for this one.
<p>
Gus Mueller chimed in saying that if you use <code>CFArrayBSearchValues</code>, be sure to sort with <code>CFArraySortValues</code> rather than using the Cocoa sorting routines (or at least the comparison) - they treat diacritic marks differently, leading to strange errors. From Gus, a quick objc addition to NSMutableArray:
<pre>
- (void) cfStringSort {
CFArraySortValues((CFMutableArrayRef)self, CFRangeMake(0, [self count]), (CFComparatorFunction)CFStringCompare, NULL);
}
</pre>
<p>
Thanks to Preston Jackson for finding a bug in the original implementation. In the case of "not found", it returns the insertion point to put this item, so if you're interested in "is this thing there", you need to compare to what was found.<p>
<li> <b>Filtering an array</b> (NSArray->General)<br>
You can use <code>NSPredicate</code> and <code>-filteredArrayUsingPredicate:</code> to selectively remove stuff from an array.
<pre>
NSPredicate *hasZone = [NSPredicate predicateWithBlock:
^BOOL (id obj, NSDictionary *bindings) {
GRProfileCueTime *cueTime = obj;
return cueTime.cue.targetHeartZone != kZoneNone;
}];
NSArray *justZones = [cues filteredArrayUsingPredicate: hasZone];
</pre>
This removes all of the elements of the array that do not have a target heart zone.
<p>
If this kind of stuff is useful for you, you might want to check out <a href="https://github.com/mikeash/MACollectionUtilities">Mike Ash's collection utilities.</a><p>
<li> <b>Multiple Objects in one document</b> (Photoshop->General)<br>
Say you have two Smart Objects from ACR or something, and now you want them into one document so you can do some layer mask nonsense. To get them into one file do this:
<ul>
<li> Open up both files
<li> Bring the source file up, shift-drag the layer from the palette
<li> Drag into the image area of the target document
</ul><p>
<li> <b>Beware BOOL results from objc_msgSend</b> (Objective-C->Random)<br>
Courtesy of David Philip Oster, from GTMNSEnumerator+Filter.m:
<pre>
// We must take care here, since Intel leaves junk in high bytes of return register
// for predicates that return BOOL.
// For details see:
// <a href="http://developer.apple.com/legacy/mac/library/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/universal_binary_tips.html#/">unibin chapter and verse</a> (link currently broken courtesy of Apple)
// and
//<a href="http://www.red-sweater.com/blog/320/abusing-objective-c-with-class#comment-83187">comment at red-sweater</a>.
- (BOOL)filterObject:(id)obj returning:(id *)resultp {
*resultp = obj;
return ((BOOL (*)(id, SEL, id))objc_msgSend)(obj, operation_, object_);
}
</pre>
The explicit cast to the expected return value gets rid of the junk.<p>
<li> <b>Ignore files</b> (Mercurial->General)<br>
Tired of build directories or .DS_Store files showing up in <code>hg status</code>? Make a <code>.hgignore</code> at the top level of your repository. It supports glob and regex, and plain names. <code>man hgignore</code> for more details.<p>
<li> <b>Supplying bitbucket username / password</b> (Mercurial->General)<br>
BitBucket provides Mercurial hosting. It's kind of a pain to have to provide a username/password on every pull or push. So, just add something like this to your <code>~/.hgrc</code> file and make sure the permissions on the file are such that nobody else can read it.
<pre>
[auth]
bitbucket.prefix = bitbucket.org/markd2
bitbucket.username = markd2
bitbucket.password = B4dg3rzRuL3
bitbucket.schemes = http https
</pre>
So now when you do a
<pre>
% hg push https://bitbucket.org/markd2/borkfit
</pre>
no need to authorize.<p>
<li> <b>Using Changes.app for hg's diffing</b> (Mercurial->General)<br>
Add this to your <code>~/.hgrc</code>
<pre>
[extensions]
extdiff =
[extdiff]
cmd.chdiff = /Local/Apps/Changes.app/Contents/Resources/chdiff
opts.chdiff = --wait
</pre>
Your Changes.app path is probably different, so be sure to change it. Or add the <code>chdiff</code>'s directory to your shell path.
<p>
Now you can<br>
<code>% hg chdiff</code><br>
And get pretty diffing.<p>
<li> <b>Changing title of Back Button</b> (UIViewController->General)<br>
Sometimes the default title of the UIViewController "Back" button isn't quite right. To change it to something else, do this:
<pre> UIBarButtonItem *backButton =
[[UIBarButtonItem alloc] initWithTitle: @"Back"
style: UIBarButtonItemStyleBordered
target: nil
action: nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
</pre>
The catch is you need to do it in the parent controller before pushing the subcontroller. You can stick this into the -init and change it once<p>
<li> <b>Making BNR-Style View Controller</b> (UIViewController->General)<br>
One of the things I like about the Big Nerd Ranch iPhone book is how they put the name of the nib file in a ViewController's implementation, rather than making all users of the ViewController know the nib file name. Here's how they do it:
<pre>
@implementation BlahViewController
- (id) init {
if ((self = [super initWithNibName: @"BlahViewController"
bundle: nil])) {
// other initialization
}
return self;
} // init
- (id) initWithNibName: (NSString *) nibNameOrNil
bundle: (NSBundle *) nibBundleOrNil {
return [self init];
} // initWithNibName
</pre><p>
<li> <b>Modal View Controllers</b> (UIViewController->General)<br>
Present a modal view controller (iOS 5 and beyond):
<pre>
[self presentViewController: vc animated: YES
completion: ^{
NSLog (@"you complete me");
}];
</pre>
and to get rid of it
<pre>
[self dismissViewControllerAnimated: YES completion: ^(void) {
NSLog (@"kompressor does not dance");
}];
</pre>
<h4>Pre-iOS 5</h4>
Present a modal view controller:
<pre>
[self presentModalViewController: webview animated: YES];
</pre>
And to get rid of it (maybe from inside of the view controller that just got modalated)
<pre>
[self dismissModalViewControllerAnimated: YES];
</pre><p>
<li> <b>Displaying a UIAlert</b> (UIAlert->General)<br>
<pre>
UIAlertView *alert =
[[UIAlertView alloc] initWithTitle: @"Some Title"
message: @"You look very nice today."
delegate: self
cancelButtonTitle: @"OK"
otherButtonTitles: nil];
[alert show];
[alert release];
</pre>
If you want to know what got tapped, use a delegate method. The cancel button is index zero.
<pre>
- (void) alertView: (UIAlertView *) alertView
clickedButtonAtIndex: (NSInteger) buttonIndex {
NSLog(@"foobage! %d", buttonIndex);
} // clickedButtonAtIndex
</pre><p>
<li> <b>Add a cancel button to a navigation bar.</b> (UINavigationController->General)<br>
In your <code>-viewDidLoad</code> :
<pre> UIBarButtonItem *cancelButton =
[[UIBarButtonItem alloc] initWithTitle: @"Cancel"
style: UIBarButtonItemStylePlain
target: self
action: @selector(cancel:)];
self.navigationItem.rightBarButtonItem = cancelButton;
[cancelButton release];</pre><p>
<li> <b>Adding a plus button to a navigation bar</b> (UINavigationController->General)<br>
<pre> UIBarButtonItem *addButton =
[[UIBarButtonItem alloc]
initWithBarButtonSystemItem: UIBarButtonSystemItemAdd
target: self
action: @selector(addNewSegment)];
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
</pre><p>
<li> <b>Adding an info button to the nav bar</b> (UINavigationController->General)<br>
<pre>UIButton *button = [UIButton buttonWithType: UIButtonTypeInfoLight];
[button addTarget: self
action: @selector(about:)
forControlEvents: UIControlEventTouchUpInside];
UIBarButtonItem *infoItem =
[[UIBarButtonItem alloc] initWithCustomView: button];
... make any other button items you want
NSArray *rightButtonItems = @[ spacer, infoItem, widerSpace, someOtherItem ];
_viewController.navigationItem.rightBarButtonItems = rightButtonItems;
</pre><p>
<li> <b>Hide the navigation bar's "Back" button.</b> (UINavigationController->General)<br>
<pre> self.navigationItem.hidesBackButton = YES;</pre><p>
<li> <b>Hiding the navigation bar</b> (UINavigationController->General)<br>
Say you had a main screen that doesn't need to show the navigation bar. When you "Back" from another view controller your delegate will get called. You can decide then whether to turn off the nav bar.
<pre>
- (void) navigationController: (UINavigationController *) navigationController
willShowViewController: (UIViewController *) viewController
animated: (BOOL) animated {
if (viewController == _menuController) {
[_navigationController setNavigationBarHidden: YES
animated: YES];
}
} // willShowViewController
</pre><p>
<li> <b>Popping the current view controller</b> (UINavigationController->General)<br>
Say the user selected a row in a tableview, and now we're done with this view controller:
<pre>
- (void)tableView: (UITableView *) tableView
didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
// might want someone to know what the user picked.
[_delegate kindChooser: self
choseWorkoutType: mapRowToWorkoutType(indexPath.row)];
// pop ourselves off the stack.
[self.navigationController popViewControllerAnimated: YES];
} // didSelectRowAtIndexPath
</pre><p>
<li> <b>Pushing a new view controller onto the stack.</b> (UINavigationController->General)<br>
Moving to another view controller from the current one.
<pre> GRChooseRideKindViewController *chooseKind =
[[GRChooseRideKindViewController alloc] init];
[self.navigationController pushViewController: chooseKind
animated: YES];
[chooseKind release];</pre><p>
<li> <b>Show navigation bar on push</b> (UINavigationController->Swift)<br>
Say you have a pretty main menu that's in a nav controller, and tapping on buttons take you to pushed view controllers. The pushed VCs need the navbar so they can go Back, but the pretty main menu doesn't need the navbar. <i>Swift 3</i>
<pre>
class PrettyMainMenuViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.delegate = self
}
}
extension PrettyMainMenuViewController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController,
willShow viewController: UIViewController,
animated: Bool) {
if viewController === self {
navigationController.setNavigationBarHidden(true, animated: true)
} else {
navigationController.setNavigationBarHidden(false, animated: true)
}
}
}
</pre><p>
<li> <b>Adding a long-press to a UITableViewCell that pops up a UIMenuController</b> (UITableView->General)<br>
To add a long-press that pops up a UIMenuController, You'll need to subclass UITableViewCell, but you're probably already doing that.
Add the gesture recognizer, and a notification handler that'll unselect the cell:
<pre> UILongPressGestureRecognizer *longPressGesture =
[[[UILongPressGestureRecognizer alloc] initWithTarget: self
action: @selector(longPress:)]
autorelease];
[self addGestureRecognizer: longPressGesture];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self
selector: @selector(unselectSelf:)
name: UIMenuControllerWillHideMenuNotification
object: nil];
- (void) unselectSelf: (NSNotification *) notification {
self.selected = NO;
} // unselectSelf
</pre>
In the long press handler, check the state (to prevent multiple firings of the gesture recognizer from making the menus spaz out), and then set up the menu thing.
<pre>- (void) longPress: (UILongPressGestureRecognizer *) recognizer {
if (recognizer.state != UIGestureRecognizerStateBegan) return;
<i>Put the stuff from <a href="http://borkware.com/quickies/single?id=537"> Adding a pop up menu thingie</a> here</i>
self.selected = YES; // Highlight to make it doubly-obvious what's being menu'd
} // longPress
</pre><p>
<li> <b>Adding an arrow thingie or checkbox to tableview cells</b> (UITableView->General)<br>
If you have the cell, you can set the accessoryType directly:
<pre>UITableViewCell *cell = ...;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;</pre>
The delegate method (accessoryTypeForRowWithIndexPath) has been deprecated, so don't use that.
<p>
Use <code>UITableViewCellAccessoryCheckmark</code> for a checkmark.
<p>
Use <code>UITableViewCellAccessoryNone</code> to remove the checkmark.<p>
<li> <b>Adding an index to a tableview</b> (UITableView->General)<br>
Some tableviews have the short-cut list thingie on the side, which apple calls the index. The index is section-based - each entry in the index corresponds to a section in your data. So if you have a pretty flat list (just an NSArray, for instance), you'll either need to map section-> index, or split up your data so it's physically organized in section/row.
<p>
And then override these. In this case, I split up an array into an array of arrays, each sub-array being the contents of a section that corresponds to a string to display in the index. <code>_tableIndex</code> is an array of strings for display of the index.
<pre>- (NSArray *) sectionIndexTitlesForTableView: (UITableView *) tableView {
return _tableIndex;
} // sectionIndexTitles
- (NSInteger) tableView: (UITableView *) tableView
sectionForSectionIndexTitle: (NSString *) title
atIndex: (NSInteger) index {
return index;
} // sectionForSectionIndexTitle</pre><p>
<li> <b>Changing UITableViewCell label text color</b> (UITableView->General)<br>
<pre>
if (someProperty) cell.textLabel.textColor = [UIColor grayColor];
else cell.textLabel.textColor = [UIColor blackColor];
</pre><p>
<li> <b>Controlling UITableView row rearranging</b> (UITableView->General)<br>
Say that you want to let the user rearrange tableview rows except for the first and the last, which can't move.
First, inhibit the drawing of the little rearrange indicator on the first and last rows:
<pre>- (BOOL) tableView: (UITableView *) tableView
canMoveRowAtIndexPath: (NSIndexPath *) indexPath {
if (indexPath.row == 0) return NO;
else if (indexPath.row == _cues.count - 1) return NO;
else return YES;
} // canMoveRowAtIndexPath</pre>
And then prevent rows from being dragged to the first and last position:
<pre>- (NSIndexPath *) tableView: (UITableView *) tableView
targetIndexPathForMoveFromRowAtIndexPath: (NSIndexPath *) source
toProposedIndexPath: (NSIndexPath *) destination {
if (destination.row > 0 && destination.row < _cues.count - 1) {
// No violence necessary.
return destination;
}
NSIndexPath *indexPath = nil;
// If your table can have <= 2 items, you might want to robusticize the index math.
if (destination.row == 0) {
indexPath = [NSIndexPath indexPathForRow: 1 inSection: 0];
} else {
indexPath = [NSIndexPath indexPathForRow: _cues.count - 2
inSection: 0];
}
return indexPath;
} // targetIndexPathForMoveFromRowAtIndexPathPleaseOHaiThanksForMovingKthxBai</pre><p>
<li> <b>Creating a new index path</b> (UITableView->General)<br>
<pre> NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row
inSection: 0];
</pre><p>
<li> <b>Filter a table view selection</b> (UITableView->General)<br>
You might have some rows in a UITableView you don't want to be selected. Override <code>willSelectRowAtIndexPath</code>, return nil to reject the selection, return the passed-in indexPath to use as-is, or return your choice of selected cells.
<pre>
- (NSIndexPath *) tableView: (UITableView *) tableView
willSelectRowAtIndexPath: (NSIndexPath *) indexPath {
// don't select the top row sort control.
if (indexPath.row == 0) return nil;
else return indexPath;
} // willSelectRowAtIndexPath
</pre><p>
<li> <b>Finding the row from a tableview cell button</b> (UITableView->General)<br>
You can add buttons to a UITableViewCell. The fun part is figuring out what row that button lives on. Assuming that the button is added right to the cell, you can look at the button's superview to get the cell, and then ask the tableview for the cell's section and row.
<pre>- (IBAction) elaborateType: (id) sender {
if (![sender isKindOfClass: [UIButton class]]) return; // be paranoid
UITableViewCell *cell = (UITableViewCell *)[sender superview];
if (![cell isKindOfClass: [UITableViewCell class]]) return; // be paranoid
NSIndexPath *indexPath = [self.kindPickerTableView indexPathForCell: cell];
// do something with indexPath.row and/or indexPath.section.
} // elaborateType</pre><p>
<li> <b>Getting selected row from UITableView</b> (UITableView->General)<br>
<pre> NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if (indexPath != nil) [self doStuff];
</pre><p>
<li> <b>Handling UITableView row deletion</b> (UITableView->General)<br>
<pre>- (void) tableView: (UITableView *) tableView
commitEditingStyle: (UITableViewCellEditingStyle) editingStyle
forRowAtIndexPath: (NSIndexPath *) indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[_cues removeObjectAtIndex: indexPath.row]; // manipulate your data structure.
[tableView deleteRowsAtIndexPaths: [NSArray arrayWithObject: indexPath]
withRowAnimation: UITableViewRowAnimationFade];
[self updateUI]; // Do whatever other UI updating you need to do.
}
} // commitEditingStyle
</pre><p>
<li> <b>Invalidating a single row</b> (UITableView->General)<br>
Sometimes you want to update a single row of a tableview as new information comes in. Such as loading something in the background and you want to update a percentage being shown in the cell. You could <code>-reloadData</code>, but don't. That's an awfully big hammer. Instead just reload a row
<pre> NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row
inSection: 0];
NSArray *array = [NSArray arrayWithObject: indexPath];
[_playlistTableView reloadRowsAtIndexPaths: array
withRowAnimation: UITableViewRowAnimationNone];</pre><p>
<li> <b>Laying out tableview cell when entering / exiting edit mode</b> (UITableView->General)<br>
Entering and exiting UITableView edit mode has a cool animation. If you've got a custom subclass, your goodies don't move unless you override <code>layoutSubviews</code> in your UITableViewCell subclass. Any view position changes will automatically be animated.
<pre>
- (void) layoutSubviews {
// skootch stuff over
if (self.editing && !self.showingDeleteConfirmation) {
#define MARGIN 40.0
CGRect frame = CGRectMake (MARGIN + 4.0, 4.0, 70.0, 46.0);
_profileView.frame = frame;
frame = CGRectMake (MARGIN + 80.0, 0, 240.0 - MARGIN, 55.0);
_titleLabel.frame = frame;
// layout normally
} else {
CGRect frame = CGRectMake (4.0, 4.0, 70.0, 46.0);
_profileView.frame = frame;
frame = CGRectMake (80.0, 0, 240.0, 55.0);
_titleLabel.frame = frame;
}
[super layoutSubviews];
} // layoutSubviews
</pre>
The <code>showingDeleteConfirmation</code> test is so you don't move things around if the user does the "swipe-right to show delete button" thing.<p>
<li> <b>Prevent editing / deleting of table rows</b> (UITableView->General)<br>
To prevent a row from being deleted in a UITableView (say a header with sorting controls), override <code>-canEditRowAtIndexPath:</code>
<pre>
- (BOOL) tableView: (UITableView *) tableView
canEditRowAtIndexPath: (NSIndexPath *) indexPath {
if (indexPath.row == 0) return NO;
else return YES;
} // canEditRowAtIndexPath
</pre><p>
<li> <b>Putting UITableView into editing mode</b> (UITableView->General)<br>
UITableView editing mode lets you delete / rearrange / insert rows. Turn it on via
<pre>
[self.cuesTableView setEditing: YES animated: YES];
</pre><p>
<li> <b>Rearranging in UITableView</b> (UITableView->General)<br>
Implementing this method in your UITableView datasource will make the table draw the little rearrange marker on each row.
<pre>
- (void) tableView: (UITableView *) tableView
moveRowAtIndexPath: (NSIndexPath *) from
toIndexPath: (NSIndexPath *) to {
// How to rearrange stuff if you're backed by an NSMutableArray:
GRCue *cue = [_cues objectAtIndex: from.row];
[cue retain]; // Let it survive being removed from the array.
[_cues removeObjectAtIndex: from.row];
[_cues insertObject: cue atIndex: to.row];
[cue release];
} // moveRowAtIndexPath
</pre><p>
<li> <b>Responding to table taps</b> (UITableView->General)<br>
Set yourself up as the UITableView delegate first.
<pre>
- (void)tableView: (UITableView *) tableView
didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
// Do something logical with indexPath.row, etc.
} // didSelectRowAtIndexPath
</pre><p>
<li> <b>Scrolling to a row in a UITableView</b> (UITableView->General)<br>
<pre> NSIndexPath *someRow = [NSIndexPath indexPathForRow: random() % blah.count
inSection: 0];
[self.cuesTableView scrollToRowAtIndexPath: someRow
atScrollPosition: UITableViewScrollPositionBottom
animated: YES];</pre>
You can also scroll to PositionTop and PositionMiddle. If you crash, make sure you've done a <code>-reloadData</code> on the table view prior to trying to scroll.<p>
<li> <b>Selecting a row in a UITableView</b> (UITableView->General)<br>
<pre>
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: index
inSection: 0];
[self.tableView reloadData]; // necessary if selecting row in -viewDidLoad
[self.tableView selectRowAtIndexPath: indexPath
animated: YES
scrollPosition: UITableViewScrollPositionNone];
</pre><p>
<li> <b>Setting a cell's background color</b> (UITableView->General)<br>
<pre>cell.textLabel.backgroundColor = [UIColor redColor];
cell.contentView.backgroundColor = [UIColor redColor];</pre><p>
<li> <b>Setting a cell's image</b> (UITableView->General)<br>
<pre> UIImage *image = [_assets iconAtIndex: indexPath.row];
if (image) cell.imageView.image = image;</pre><p>
<li> <b>Table view data source foobage</b> (UITableView->General)<br>
Boilerplate for UITableView DataSource stuff.
<pre>
// Optional
- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView {
return 1;
} // numberOfSectionsInTableView
- (NSInteger) tableView: (UITableView *) tableView
numberOfRowsInSection: (NSInteger) section {
return dataArray.count;
} // numberOfRowsInSection
- (UITableViewCell *) tableView: (UITableView *) tableView
cellForRowAtIndexPath: (NSIndexPath *) indexPath {
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier: @"BlahTableViewCell"];
if (!cell) {
cell = [[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: @"BlahTableViewCell"];
[cell autorelease]; // Delete for ARC
}
cell.textLabel.text = [dataArray objectAtIndex: indexPath.row];
return cell;
} // cellForRowAtIndexPath
</pre><p>
<li> <b>Tableview subtitle cell</b> (UITableView->General)<br>
The subtitle cell has a large label and a smaller gray-text sublabel.
<p>
Make the cell with <code>UITableViewCellStyleSubtitle</code>:
<pre> cell = [[[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleSubtitle
reuseIdentifier: @"UITableViewCell"] autorelease];
</pre>
and set the label values:
<pre>
cell.textLabel.text = @"somewhere";
cell.detailTextLabel.text = @"over the rainbow";
</pre><p>
<li> <b>Turning off UITableView scrolling</b> (UITableView->General)<br>
<code>_tableView.scrollEnabled = NO;</code><p>
<li> <b>UITableView section header titles</b> (UITableView->General)<br>
UITableView won't display its groovy section headers (or footers) until you supply them via the datasource:
<pre>
- (NSString *) tableView: (UITableView *) tableview
titleForHeaderInSection: (NSInteger) section {
NSArray *sections = [BWTermStorage sections];
return [sections objectAtIndex: section];
} // titleForHeaderInSection
</pre>
You can return a title for a footer.
<p>
You can also return a view:
<pre>
- (UIView *) tableView: (UITableView *) tableView
viewForHeaderInSection: (NSInteger) section;
</pre><p>
<li> <b>UITableViewCell styles</b> (UITableView->General)<br>
I always forget what each different kind of tableview cell looks like. Images shamelessly stolen from <a href="https://developer.apple.com/library/safari/#documentation/UserExperience/Conceptual/MobileHIG/UIElementGuidelines/UIElementGuidelines.html">MobileHIG</a>.
<p>
The appropriate constants are
<code>UITableViewCellStyleDefault</code>, <code>UITableViewCellStyleValue1</code>, <code>UITableViewCellStyleValue2</code>, and <code>UITableViewCellStyleSubtitle</code>, and settable properties include <code>imageView</code>, <code>textLabel</code>, and <code>detailTextLabe</code>l.
<p>
<img src="files/cell-styles.png"><p>
<li> <b>Using a nib for a tableview cell</b> (UITableView->General)<br>
You can use a nib file for table view cells pretty easily in iOS5.
<p>
1) Make a nib with a single object at the top, a <code>UITableViewCell</code>. Lay it out as you wish. I use view tags to get to the objects contained therein.
<pre>
enum {
kTermLabelTag = 1,
kDetailLabelTag = 2
};
</pre>
<p>
2) Register the nib for a cell reuse identifier. I do it in my <code>-viewDidLoad</code>.
<pre>
static NSString *g_cellReuseIdentifier = @"BWLookupCellReuseIdentifier"; // file global
...
UINib *nib = [UINib nibWithNibName: @"BWLookupTableViewCell" bundle: nil];
[self.resultsView registerNib: nib
forCellReuseIdentifier: g_cellReuseIdentifier];
</pre>
3) Dequeue the cell as usual
<pre>
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier: g_cellReuseIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleSubtitle
reuseIdentifier: g_cellReuseIdentifier];
}
</pre>
4) Dig into the cell with the tags, and go nuts
<pre>
UILabel *termLabel = (UILabel *)[cell viewWithTag: kTermLabelTag];
UILabel *detailLabel = (UILabel *)[cell viewWithTag: kDetailLabelTag];
termLabel.text = termString;
detailLabel.text = definition;
</pre><p>
<li> <b>Clipping cell image drawing to rounded corners of grouped tables</b> (UITableView->Hacks)<br>
If you have a grouped UITableView, the top and bottom cells of each group are rounded. If you have an image in the cell, it will obliterate the left-hand corners. There's a number of ways to properly fix this : pre-clip your images with some transparency at the corners. Set up a clipping path before drawing the cell, or playing with the image view's cell layer.
<p>
Or you can hack it by setting the cell's indent to 1. That scoots the whole cell contents over by 10 pixels, letting you miss the rounded corners. Really handy if you're prototyping, want something to look non-horrible, but don't want to go the full-blown correct solution.<p>
<li> <b>Preparing for Segue from a UITableView tap</b> (UITableView->Swift)<br>
<pre>
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let selectedRow = self.tableView.indexPathForSelectedRow else { return }
guard let vc = segue.destination as? InfographicViewController else { return }
let counter = counters[selectedRow.row]
let stats = counter.currentTextStats
vc.stats = stats
}
</pre><p>
<li> <b>UITableView data source</b> (UITableView->Swift)<br>
<pre>
@IBOutlet var sessionsTableView : UITableView!
let cellReuseIdentifier = "Cell"
...
override func viewDidLoad() {
super.viewDidLoad()
self.sessionsTableView.register(UITableViewCell.self,
forCellReuseIdentifier: cellReuseIdentifier)
}
...
extension ViewController : UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier,
for: indexPath) as UITableViewCell
cell.textLabel?.text = "Spork (indexPath.row)"
return cell
}
}
</pre><p>
<li> <b>Adding a target/action to a UIButton</b> (UIButton->General)<br>
<pre>
[playButton addTarget: self
action: @selector(play)
forControlEvents: UIControlEventTouchUpInside];
</pre><p>
<li> <b>Changing UIButton image</b> (UIButton->General)<br>
<pre>[_flobButton setImage:[UIImage imageNamed: @"greezole"]
forState: UIControlStateNormal];</pre><p>
<li> <b>Changing UIButton title</b> (UIButton->General)<br>
<pre>[_rejectButton setTitle: @"My Spoon is Too Big" forState: UIControlStateNormal];
</pre><p>
<li> <b>Changing UIButton title color</b> (UIButton->General)<br>
<pre>[self.z1Button setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];</pre><p>
<li> <b>Core Animation layer style properties</b> (Core Animation->General)<br>
I can never remember the link to <strike><a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/LayerVisProps.html">the Core Animation Layer Style Properties</a></strike>
<a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/LayerStyleProperties/LayerStyleProperties.html">Layer Style Property Animations</a> (probably because Apple keeps moving it)
<p>
You might need to include this too:
<pre>#import <QuartzCore/QuartzCore.h></pre><p>
<li> <b>Implicit Core Animation, with cleanup</b> (Core Animation->General)<br>
<pre>
[UIView animateWithDuration: kZoomAnimationDuration
animations: ^{
CGRect frame = self.labelZoomTapView.frame;
self.zoomedLabelView.frame = frame;
// and change whatever other properties that will lead to animation.
} completion: ^(BOOL finished) {
// or whatever cleanup you have to do
[self.zoomedLabelView removeFromSuperview];
}];
</pre><p>
<li> <b>Rotating a layer by 90 degrees</b> (Core Animation->General)<br>
To rotate a 'flat' layer, gotta convert degrees to radians, and then rotate around a vector along the z axis.
<pre>
hooverLayer.transform = CATransform3DRotate(CATransform3DIdentity,
90.0 * M_PI / 180.0,
0.0f, 0.0f, 1.0f);
</pre><p>
<li> <b>Breaking on a particular caller</b> (lldb->General)<br>
Say that we have a <code>borkFunction</code>, and it's called by <code>greebleFunction</code> and <code>hooverFunction</code>:
<pre> greebleFunction ();
hooverFunction ();</pre>
And say that we want to break when <code>borkFunction</code> is called by hoover, and not by greeble.
<pre>
(lldb) <b>breakpoint set -n borkFunction</b>
Breakpoint created: 1: name = 'borkFunction', locations = 1
(lldb) <b>breakpoint command add --script-type python 1</b>
Enter your Python command(s). Type 'DONE' to end.
> <b>thread = frame.GetThread()</b>
> <b>caller = thread.GetFrameAtIndex(1)</b>
> <b>if caller.GetFunctionName() != "hooverFunction":</b>
> <b> process = thread.GetProcess()</b>
> <b> process.Continue()</b>
> <b>DONE</b>
(lldb) <b>run</b>
greeble!
bork!
hoover!
Process 9074 stopped
* thread #1: tid = 0x2007, 0x0000000100000dff funcs`borkFunction + 15 at funcs.m:7,
stop reason = breakpoint 1.1
...
(lldb) <b>thread backtrace</b>
frame #0: 0x0000000100000dff funcs`borkFunction + 15 at funcs.m:7
frame #1: 0x0000000100000e5e funcs`hooverFunction + 30 at funcs.m:19
...
</pre><p>
<li> <b>Setting environment variables</b> (lldb->General)<br>
<pre>(lldb) settings set target.process.env-vars OOP=ack</pre><p>
<li> <b>Deserializing with TouchJSON</b> (JSON->General)<br>
<pre> #import "CJSONDeserializer.h"
NSData *jsonData = [NSData dataWithContentsOfFile: path]; // or from network, or whatever
NSError *error;
NSArray *playlists =
[[CJSONDeserializer deserializer] deserializeAsArray: jsonData
error: &error];
</pre><p>
<li> <b>Serializing with TouchJSON</b> (JSON->General)<br>
<pre> #import "CJSONSerializer.h"
NSArray *allSongs = [self allSongs];
NSString *jsonString = [[CJSONSerializer serializer] serializeObject: allSongs];
NSData *data = [jsonString dataUsingEncoding: NSUTF8StringEncoding];
// write |data| to a file, send over the network, etc
</pre><p>
<li> <b>Selecting a row in a UIPickerView</b> (UIPickerView->General)<br>
<pre>
[_durationPicker selectRow: row
inComponent: 0
animated: NO];
</pre><p>
<li> <b>UIPickerView delegate and datasource methods</b> (UIPickerView->General)<br>
<pre>
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
- (NSInteger) pickerView: (UIPickerView *) picker
numberOfRowsInComponent: (NSInteger) component
- (NSString *) pickerView: (UIPickerView *) picker
titleForRow: (NSInteger) row
forComponent: (NSInteger) component
- (void) pickerView: (UIPickerView *) picker
didSelectRow: (NSInteger) row
inComponent: (NSInteger) component
</pre><p>
<li> <b>Are we compiling for the device, simulator, or other?</b> (Building->General)<br>
<pre>// Are we compiling for the device, simulator, or desktop?
#if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
#define GR_TARGET_SIMULATOR 1
#elif TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
#define GR_TARGET_DEVICE 1
#else
#define GR_TARGET_DESKTOP 1
#endif</pre>
Then I use the GR_TARGET symbols for #if'ing in platform-specific chunklets of code.<p>
<li> <b>Making object subclasses appear in NSSets</b> (NSSet->General)<br>
Have an object you've put into an NSSet, and things don't seem to be working? Make sure you implement <code>-isEqual:</code> and <code>-hash</code>
<pre>- (BOOL) isEqual: (id) object;
- (NSUInteger) hash;</pre><p>
<li> <b>Getting UILabel to word-wrap in IB.</b> (UILabel->General)<br>
You can get a UILabel to word-wrap its contents in IB. The trick is to set "Line Breaks" to "Word Wrap" <b>and</b> "Lines" to zero.<p>
<li> <b>Adding gesture recognizers</b> (UIView->General)<br>
Gesture recognizers are cool. Here is how you can add them to a UIView, say at init or view loading time:
<pre> UITapGestureRecognizer *tap =
[[UITapGestureRecognizer alloc] initWithTarget: self
action: @selector(twoByThreeTap:)];
tap.numberOfTapsRequired = 2;
tap.numberOfTouchesRequired = 3;
[self addGestureRecognizer: tap];
[tap release];</pre>
If you're not getting taps, perhaps you're using a default UIImageView, you'll probably need to turn on interaction and multitouch, either in IB or in code:
<pre> // So we can get the taps.
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = YES;</pre><p>
<li> <b>Converting touch coordinate to the view</b> (UIView->General)<br>
<pre>- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView: self];
if (CGRectContainsPoint(self.bounds, point)) {
NSLog (@"YAY!");
}
} // touchesEnded
</pre><p>
<li> <b>Discriminating single and double tap gesture</b> (UIView->General)<br>
You want a single tap to do something, and a double tap to do something else. You don't want the single tap to fire unless it's fer sher not a double-tap. (assuming you can live for the timeout before your single tap gesture happens). You can make the single-tap recognizer wait until the double-tap one fails:
<pre> UITapGestureRecognizer *singleTap =
[[UITapGestureRecognizer alloc] initWithTarget: self
action: @selector(startStop:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: singleTap];
UITapGestureRecognizer *doubleTap =
[[UITapGestureRecognizer alloc] initWithTarget: self
action: @selector(resetTimer:)];
doubleTap.numberOfTapsRequired = 2;
doubleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: doubleTap];
[singleTap requireGestureRecognizerToFail: doubleTap];
[singleTap release];
[doubleTap release];
</pre><p>
<li> <b>Displaying stuff on a monitor hooked up to the phone</b> (UIView->General)<br>
<pre>BOOL haveSecondScreen = [UIScreen screens].count > 1;
if (haveSecondScreen) {
UIScreen *screen = [[UIScreen screens] objectAtIndex: 1];
// Figure out the largest screen mode we can use.
CGSize max = CGSizeMake (0.0, 0.0);
UIScreenMode *maxScreenMode = nil;
for (UIScreenMode *mode in [screen availableModes]) {
if (mode.size.width * mode.size.height > max.width * max.height) {
max = mode.size;
maxScreenMode = mode;
}
}
screen.currentMode = maxScreenMode;
UIView *view = [[UIView alloc] initWithFrame: screen.bounds];
view.backgroundColor = [UIColor greenColor];
UIWindow *window = [[UIWindow alloc] init];
window.screen = screen;
[window addSubview: view];
window.hidden = NO;
[view release];
// Stash |window| into an ivar or something.
}</pre>
This doesn't deal with stuff like aspect ratios, etc.<p>
<li> <b>Make a UIView round-cornered with a border</b> (UIView->General)<br>
<pre>
self.<i>groovyView</i>.layer.cornerRadius = 5;
self.<i>groovyView</i>.layer.borderColor = [[UIColor purpleColor] CGColor];
self.<i>groovyView</i>.layer.borderWidth = 1;
self.<i>groovyView</i>.layer.masksToBounds = YES;
</pre><p>
<li> <b>Put a border around a UITextView</b> (UIView->General)<br>
(or around any UIView for that matter)
<pre>
#import <QuartzCore/QuartzCore.h> // For layer styles
...
textview.layer.borderWidth = 1.0f;
textview.layer.borderColor = [UIColor blackColor].CGColor;
</pre><p>
<li> <b>The UIResponder touch methods</b> (UIView->General)<br>
<pre>
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event;
- (void) touchesMoved: (NSSet *) touches withEvent: (UIEvent *) event;
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event;
- (void) touchesCancelled: (NSSet *) touches withEvent: (UIEvent *) event;
</pre><p>
<li> <b>Unique looking custom placeholder views</b> (UIView->Hacks)<br>
Every now and then I want a placeholder view that just draws itself. In this particular case, I wasn't sure where the contents of various view controllers were being set up, and whether it was re-using views or making them new each time. I added one of these to the view hierarchy. If I saw it, I know where it was created. Because the color is based on the view's address, each view will get a different color, making it easy to see if distinct views are being used or if one is being recycled.
<pre>
@interface BlahView : UIView
@end
@implementation BlahView
- (void) drawRect: (CGRect) rect {
CGRect bounds = self.bounds;
UIColor *color = [UIColor colorWithRed: (((int)self >> 0) & 0xFF) / 255.0
green: (((int)self >> 8) & 0xFF) / 255.0
blue: (((int)self >> 16) & 0xFF) / 255.0
alpha: 1.0];
[color set];
UIRectFill(bounds);
[[UIColor blackColor] set];
UIRectFrame(bounds);
} // drawRect
@end // BlahView
...
CGRect frame = CGRectMake(200, 5, 300, 30);
BlahView *view = [[BlahView alloc] initWithFrame: frame];
[viewController.view addSubview:view];
</pre>
And here is the version for Cocoa-non-touch:
<pre>
@interface BlahView : NSView
@end
@implementation BlahView
- (void) drawRect: (CGRect) rect {
CGRect bounds = self.bounds;
NSColor *color = [NSColor colorWithDeviceRed: (((int)self >> 0) & 0xFF) / 255.0
green: (((int)self >> 8) & 0xFF) / 255.0
blue: (((int)self >> 16) & 0xFF) / 255.0
alpha: 1.0];
[color set];
NSRectFill(bounds);
[[NSColor blackColor] set];
NSFrameRect (bounds);
} // drawRect
@end // BlahView
</pre>
And for Swift/Cocoa-touch:
<pre>
func prettyColor<T>(instance: T) -> UIColor {
var address = unsafeBitCast(instance, Int.self)
let red = CGFloat(address >> 0 & 0xFF) / 255.0
let green = CGFloat(address >> 8 & 0xFF) / 255.0
let blue = CGFloat(address >> 16 & 0xFF) / 255.0
let derivedColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
return derivedColor
}
</pre> (thanks to Step Christopher for the translation)<p>
<li> <b>Block Typedef</b> (Blocks->General)<br>
I can never remember the syntax for function pointer typedefs, and therefore can never remember the syntax for block typedefs:
<pre>typedef CGRect (^GroovyBlock)(NSInteger spoon, NSString *waffle);</pre>
Creates a typedef called GroovyBlock that returns a CGRect and takes an integer and a string.<p>
<li> <b>Block syntax for properties</b> (Blocks->General)<br>
Want to have a block as a <code>@property</code>? This declares a property named <code>dataUpdater</code> that's a block that takes two arguments.
<p>
<pre>
@property (copy, nonatomic) void (^dataUpdater)(NSString *key, id newValue);
</pre><p>
<li> <b>Performing one-time initialization</b> (Blocks->General)<br>
You can use libdispatch / Grand Central Dispatch, to execute a block that must run once, and only once, no matter how many threads are knocking at its door.
<pre>
+ (void) initialize {
<b>static dispatch_once_t init_predicate;
dispatch_once (&init_predicate, ^{ </b>
CGColorSpaceRef rgbSpace = CGColorSpaceCreateDeviceRGB ();
for (int i = 0; i < 7; i++) {
CGFloat components[] = { rawZoneColors[i]._red,
rawZoneColors[i]._green,
rawZoneColors[i]._blue,
rawZoneColors[i]._alpha };
zoneColors[i] = CGColorCreate (rgbSpace, components);
}
});
} // initialize
</pre><p>
<li> <b>Error code -12780</b> (Audio->General)<br>
Sometimes you get error code -12780, and it's completely undocumented ( rdar://20194243 - please document this stupid code plzkthx).
<p>
Thanks to Dave DeLong: <i>Its a CoreMedia error: Returned when caller passes incorrect input or output parameters</i>
<p>
So it's a paramErr, but for CoreMedia. Something, somewhere, internally went amiss, and it's interpreting an argument as invalid.
<p>
Here are situations I've either encountered or seen on the net:
<ul>
<li> Passing a nil AVAsset due to a MPMediaItem not being local. Workaround is to download from the cloud. No way to force a download
<li> Your audio session category is incorrect for whatever it is you're doing
<li> <a href="http://stackoverflow.com/questions/14650607/avassetexportsession-fails-every-time-error-12780">You may need to convert a path based on NSSearchPathForDirectoriesInDomains explicitly to a file URL</a>
<li> <a href="http://stackoverflow.com/questions/10858793/consecutive-calls-to-startrecordingtooutputfileurl">Consecutive calls to startRecordingToOutputFileURL</a>.
<li> <a href="http://stackoverflow.com/questions/7535113/error-recording-to-movie-file-with-avfoundation">Recording video with device orientation of UIDeviceOrientationFaceUp, UIDeviceOrientationFaceDown, or UIDeviceOrientationUnknown</a>.
<li> Trying to play Protected music (even though the AVAsset returns NO from hasProtectedContent. Sigh).
<li> <i>Got any more? let me know.</i>
</ul><p>
<li> <b>Getting the duration of an audio file</b> (Audio->General)<br>
<pre>#import <AudioToolbox/AudioToolbox.h>
...
- (NSTimeInterval) voiceCueLength {
NSTimeInterval length = 0.0;
NSURL *audioURL = [NSURL fileURLWithPath: self.pathToVoiceCue];
OSStatus result = noErr;
AudioFileID audioFile = NULL;
result = AudioFileOpenURL ((CFURLRef)audioURL,
kAudioFileReadPermission,
0, // hint
&audioFile);
if (result != noErr) goto bailout;
//Get file length
NSTimeInterval seconds;
UInt32 propertySize = sizeof (seconds);
result = AudioFileGetProperty (audioFile,
kAudioFilePropertyEstimatedDuration,
&propertySize,
&seconds);
if (result != noErr) goto bailout;
length = seconds;
bailout:
if (audioFile) AudioFileClose (audioFile);
return length;
} // voiceCueLength
</pre>
And Link with the AudioToolbox framework.<p>
<li> <b>Adding a decimal point to the numeric keyboard</b> (UITextField->General)<br>
Apple added a decimal pad keyboard type. As of Xcode 4.1, not exported in the UI, so you have to set it in code:
<pre>
textField.keyboardType = UIKeyboardTypeDecimalPad;
</pre><p>
<li> <b>Bringing up the phone keyboard</b> (UITextField->General)<br>
<pre> [self.nameField becomeFirstResponder];</pre>
If you want the keyboard to animate in after your view appears, you can call this in your <code>-viewWillAppear:</code><p>
<li> <b>Dismissing the on-screen keyboard</b> (UITextField->General)<br>
If you know the text field that is being edited, you can tell it to resign first responder:
<pre>
[textField resignFirstResponder];
</pre>
Otherwise, you can tell the enclosing view to end editing, and it'll figure out who is editing and tell them to resign:
<pre>
[self.view endEditing: YES];
</pre><p>
<li> <b>Getting notified of UITextField changes</b> (UITextField->General)<br>
Easiest way is to hook up the <i>Editing Changed</i> (<b>NOT</b> <i>Value Changed</i>) action.<p>
<li> <b>Handling keyboard return button</b> (UITextField->General)<br>
Notifications about the UITextField keyboard return button come through this delegate method. This one hides the keyboard when the return button is typed.
<pre>
- (BOOL) textFieldShouldReturn: (UITextField *) textField {
// Dismiss the keyboard.
[self.view endEditing:YES];
return YES;
}
</pre><p>
<li> <b>Only allowing numbers in a text field</b> (UITextField->General)<br>
Sometimes you need a more general keyboard for a numeric-only text field. You can do something like this in a delegate method:
<pre>
- (BOOL) textField: (UITextField *) textField
shouldChangeCharactersInRange: (NSRange) range
replacementString: (NSString *) string {
NSString *resultingString = [textField.text stringByReplacingCharactersInRange: range
withString: string];
if (resultingString.length == 0) return YES;
NSScanner *scanner = [NSScanner scannerWithString: resultingString];
float throwaway;
BOOL scansFloat = [scanner scanFloat: &throwaway];
BOOL atEnd = [scanner isAtEnd];
return scansFloat && atEnd;
} // shouldChangedCharacersInRanges
</pre>
(thanks to Frank Shearer, from a <a href="http://stackoverflow.com/questions/1320295/iphone-how-to-check-that-a-string-is-numeric-only/1750872#1750872">SO</a> post)<p>
<li> <b>Get listing of a class's methods</b> (Python->General)<br>
<code>dir (lldb.SBFrame)</code><p>
<li> <b>Quick processing of json.</b> (Python->General)<br>
Have a blorb of well-formed json you want to run some quick processing on? I've been using this little python testbed. No error checking or robustness, but real quick to dig in to data.
<pre>
#!/usr/bin/python
import json
from sys import argv
# run with the name of the json file to process.
script, filename = argv
with open(filename) as json_file:
jsoncontents = json.load(json_file)
# how to dig into the json:
upload = jsoncontents["upload"]
metadata = upload["rideMetaData"]
# print("planned ride: %s" % metadata["plannedRideMinutes"])
ridedata = upload["rideData"]
for blurb in ridedata:
if blurb["t"] == "hrt":
value = blurb["v"]
print(value)
</pre><p>
<li> <b>Describe table in sqlite3</b> (sqlite3->General)<br>
To see the table structure, use <code>.schema</code>
<pre>
sqlite> <b>.schema camp</b>
CREATE TABLE camp (code INTEGER, label TEXT, state TEXT, type TEXT, phone TEXT, sites INTEGER, nforg TEXT, lat NUMERIC, lon NUMERIC, elevation NUMERIC, fee TEXT);
CREATE INDEX pkdex ON camp (code);
</pre><p>
<li> <b>Seeing entitlement and certification information</b> (Provisioning Hell->Hacks)<br>
Sometimes it's useful to see what certificate was used for signing, and what entitlements are in your app. The <code>codesign</code> tool does that.
How to use:
<pre>
% cd ~/Library/Developer/Xcode/Archives/$DATE/Campwhere.xcarchive/Products/Applications
% codesign -dvvv ./Campwhere.app
% codesign -d --entitlements - ./Campwhere.app
</pre><p>
<li> <b>Seeing the signing jazz inside of an IPA</b> (Provisioning Hell->Hacks)<br>
An IPA is actually a zip file. You can extract the contents by unzipping it:
<pre>
% <b>unzip Catalog.ipa</b>
</pre>
Stuff will appear in a directory called "Payload"
<p>
Then you can look at signing:
<pre>
% <b>codesign -dvvv Payload/Catalog.app/</b>
Executable=/Users/markd/junk/Payload/Catalog.app/Catalog
Identifier=com.bignerdranch.Catalog
Format=bundle with Mach-O thin (armv7)
CodeDirectory v=20100 size=2513 flags=0x0(none) hashes=117+5 location=embedded
...
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Aug 30, 2012 5:21:26 PM
Info.plist entries=28
Sealed Resources rules=3 files=31
Internal requirements count=1 size=296
</pre>
as well as entitlements:
<pre>
% <b>codesign -d --entitlements - ./Payload/Catalog.app</b>
Executable=/Users/markd/junk/Payload/Catalog.app/Catalog
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
...
</pre><p>
<li> <b>Adding a tag</b> (git->Hacks)<br>
Adding a tag in git:
<pre>
% git tag '0.1-demo' -m "Proof of concept demo for initial App47 distribution"
</pre>
Adds the tag, with the given comment.
<p>
To push the tag up to github (or wherever)
<pre>
% git push --tags
</pre><p>
<li> <b>Deleting a remote git branch</b> (git->Hacks)<br>
Say you had a branch name "protocol-play" that's been pushed up to github. To remove it locally you do the usual <code>git branch -D protocol-play</code> (the capital D if you don't care about the merged status). You might need to <code>git push</code>. too.
<p>
It's gone locally, but still exists up on github. Nuke it there with:
<pre>
% git push origin :protocol-play
</pre><p>
<li> <b>Diff added changes</b> (git->Hacks)<br>
You did a <code>git add</code> to stage a change, but now you want to see what that was. Use
<pre>
% git diff --cached
</pre><p>
<li> <b>Git Logging Options</b> (git->Hacks)<br>
<code>git log [options] [--] [path]</code>
<p>
<ul>
<li> <code>-p</code> — show the diff (patch)
<li> <code>-#</code> — limit output (e.g. <code>-23</code> to last 23 entries)
<li> <code>--since / --until</code> — commits more recent (or older) than given date
<li> <code>--stat</code> — abbreviated stats (adds/deletes, modified files)
<li> <code>--pretty=<i>thing</i></code> — Make output pretty. Things include oneline, short, medium, full, fuller, (no fullest), email, raw, format:, tformat:<string> . use tformat if you want a newline at the end of the log
<li> <code>--graph</code> — draw those funky git branch gtaphs
<li> <code>--author / --committer</code> — filter on specific dude / dudette
<li> <code>--grep</code> — filter on keywords in commit messages
<li> <code>--all-match</code> — Turn multiple predicates from OR into AND
</ul>
<p>
<b>Format Options</b>
<p>
<ul>
<li> <code>%H / %h</code> — hash (shortened)
<li> <code>%T / %t</code> — tree hash (shortened)
<li> <code>%P / %p</code> — parent hash (shortened)
<li> <code>%an / %ae / %ad / %ar</code> — Author name, email, date (relative)
<li> <code>%cd / %ce / %cd / %cr</code> — Committer name, email, date (relative)
<li> <code>%n</code> — newline
</ul>
<p>
Date format can be specific (1961-01-13), or relative ("4 years 23 months"). Can replace spaces with dots: 4.years.23.months. The actual format is undocumented (OF COURSE), but you can look at the <a href="https://github.com/git/git/blob/master/date.c">approxidate code</a>.<p>
<li> <b>Git rollback</b> (git->Hacks)<br>
Commit something (before pushing), and regret it?
<pre>
% git reset --soft "HEAD~1"
</pre><p>
<li> <b>Making a new remote branch</b> (git->Hacks)<br>
Say you cloned something from github, and you're wanting to make your own branch, and have it out on github land for off-site pushes
<pre>
git branch markd-dev
git push -u origin markd-dev
</pre><p>
<li> <b>Moving a git tag to another revision.</b> (git->Hacks)<br>
github releases are based on tags. Sometimes when polishing a release you want to point it to a new tag. This is what I've used before:
<pre>
% git tag -d <i>release-cg-pt3</i>
% git push origin :refs/tags/<i>release-cg-pt3</i>
% git status
% git tag <i>release-cg-pt3 7f05a3de66</i>
% git push origin master --tags
</pre>
And the go to your github release page. It should say that the release has become a draft (due to the tag change). Edit it and republish it.<p>
<li> <b>Moving a tag</b> (git->Hacks)<br>
To move an existing git tag to the current version in your working tree:
<pre>
% git tag --force '0.1-demo' -m "pithy comment"
And if you're using github or something
% git push --tags
</pre><p>
<li> <b>Password-less github gitting</b> (git->Hacks)<br>
Typing in your username and password for github is annoying. You'll need to do the ssh key dance. <a href="http://help.github.com/mac-set-up-git/">instructions here</a>.
<p>
If your current checkout is via https (you can figure that out by doing <code>git config -l</code> (ell) and looking at the <code>remote.origin.url</code>). If it's <code>https</code> it's not going to use your ssh keys. You'll need to change it.
<p>
If it was originally:
<p>
<code>remote.origin.url=https://github.com/someuser/GroovyProject.git</code></code>
<p>
You'd do:
<p>
<code>% git config remote.origin.url git@github.com:someuser/GroovyProject.git</code><p>
<li> <b>Pruning branches</b> (git->Hacks)<br>
After a while, stale branches can pile up, especially if you're pulling and pushing to a busy repo. Get rid of them locally with:
<pre>
% <b>git remote prune --dry-run origin</b>
</pre>
To see what'll get removed, and then
<pre>
% <b>git remote prune origin</b>
</pre>
To clean things out.<p>
<li> <b>Pushing only default branch by default</b> (git->Hacks)<br>
By default <code>git push</code> tries to push all the branches, sometimes eliciting this wonderful response:
<pre><i>error: failed to push some refs to 'git@github.com:bignerdranch/roominant.git'
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. If you did not intend to push that branch, you may want to
hint: specify branches to push or set the 'push.default' configuration
hint: variable to 'current' or 'upstream' to push only the current branch.
hint: you might as well just give up. neener neener neener. love, Git
</i></pre>
So you can tell git to only push the currently active branch:
<pre>% git config --global push.default current</pre><p>
<li> <b>Three faces of git diff</b> (git->Hacks)<br>
<code>git diff</code> shows changes made since the file was last staged. If you make changes, stage them, then make more changes, this only shows you the more changes
<p>
<code>git diff --staged</code> shows changes made to the file when it was staged, diffs against the committed version it's based off of. It doesn't include any changes you've made since staging.
<p>
<code>git diff HEAD</code> (where HEAD is the name of a commit - HEAD to compare with the latest commit, or a branch name to compare to the tip of that branch. In essence, unions <code>git diff</code> and <code>git diff HEAD</code><p>
<li> <b>Undo the last commit</b> (git->Hacks)<br>
Accidentally added one too many files to the last commit (or some similar travesty). If you haven't pushed it up to github or somewhere else, you can roll it back with
<pre>git reset --soft HEAD~1</pre><p>
<li> <b>git "revert"</b> (git->Hacks)<br>
To remove uncommitted changes so you can pull without complaint (revert in svn-speak):
<pre>
% git checkout filename-to-revert
</pre>
This checks the file out from HEAD, removing the local modificaiton. (put <code>--</code> before the file name in case you have a branch named the same as the file)<p>
<li> <b>Directory listing of your container</b> (iCloud->NSFIleManager)<br>
Metadata query not coming back with anything. Is there actually any Stuff up in the cloud? Get a listing of the ubiqutious container dealie:
<pre>
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *ubiquitousFolder = [fm URLForUbiquityContainerIdentifier:nil];
NSLog (@"Location of ubiquitous folder: %@", ubiquitousFolder);
// Then use standard ways of iterating, given a URL, such as
NSArray *contents = [fm contentsOfDirectoryAtURL: ubiquitousFolder
includingPropertiesForKeys: @[]
options: 0
error: &error];
if (contents == nil) {
NSLog (@"could not contents of %@ - %@", mediaDirectory, error);
}
</pre><p>
<li> <b>Drawing wrapped text in CoreText</b> (Core Text->General)<br>
Say you have a string, and you just want to draw it pretty plain, but wrapped in
a rectangle. CoreText can do that for you:
<pre>- (void) drawText: (NSString *) text inRect: (CGRect) rect {
CTFontRef font = CTFontCreateWithName (CFSTR("Helvetica"), 15.0, NULL);
NSDictionary *attributes =
@{ (__bridge id)kCTFontAttributeName : (__bridge id) font };
CFAttributedStringRef attrString =
CFAttributedStringCreate (kCFAllocatorDefault,
(__bridge CFStringRef) text,
(__bridge CFDictionaryRef) attributes);
CTFramesetterRef fsetter = CTFramesetterCreateWithAttributedString (attrString);
CGPathRef path = CGPathCreateWithRect (rect, NULL);
CTFrameRef frame = CTFramesetterCreateFrame (fsetter,
CFRangeMake (0, 0),
path, NULL);
CGContextRef context = ...;
CGContextSetTextMatrix (context, CGAffineTransformIdentity);
CTFrameDraw (frame, context);
CFRelease (font);
CFRelease (attrString);
CFRelease (fsetter);
CGPathRelease (path);
} // drawTextInRect</pre><p>
<li> <b>Getting wrapped height from CoreText</b> (Core Text->General)<br>
You're drawing <a href="http://borkware.com/quickies/single?id=535">wrapped text</a> in a simple rectangle in CoreText, and you want to figure out the height of it. (fixed width, flexible height).
<pre> CTFramesetterRef fsetter = CTFramesetterCreateWithAttributedString (attrString);
CFRange fitRange;
CGSize frameSize =
CTFramesetterSuggestFrameSizeWithConstraints (fsetter,
CFRangeMake (0, 0),
NULL, // frame attributes
CGSizeMake (rect.size.width,
CGFLOAT_MAX),
&fitRange);
</pre>
<code>fitRange</code> is the range of characters that fit in. Given a height of CGFLOAT_MAX, this should be the entire string. <code>frameSize</code> is the width and height the text will be wrapped in. A <code>{ 0, 0}</code> range means to use the whole string.<p>
<li> <b>Fixing "file is set-id or unreadable" with space-enabled file names</b> (DTrace->General)<br>
You might want to run dtrace against an program, which might has a space in the name:
<pre>dtrace -n 'objc$target:NSUndoManager*:+alloc:entry' -c `pwd`/eClicker Presenter</pre>
And then you get the smackdown:
<pre>
dtrace: failed to execute /Users/markd/Library/Developer/.../eClicker: file is set-id or unreadable
[Note: the '-c' option requires a full pathname to the file]
</pre>
But it's right there! The path must be getting passed around, and needs some extra backslashes:
<pre>
dtrace -v -n 'objc$target:NSUndoManager*:-init*:entry' -c ./eClicker\\\ Presenter
</pre>
(In case the quickies stripped off the backslashes, it's three backslashes, then a space, then <code>Presenter</code>)<p>
<li> <b>Fixing DIF offset error</b> (DTrace->General)<br>
Sometimes you might see this error when using <code>copyin</code> or <code>copyinstr</code>:
<pre>invalid address (0x10c86e3ce) in action #2 at DIF offset</pre>
This happens because the page in question hasn't been faulted in yet, or in general isn't available to the kernel or DTrace at this moment. This can happen if you're accessing the data in an <code>:::entry</code> clause before the data is used.
<p>
To work around this, let the function you're tracing do its work, cause the fault of the data into memory, then access the data in the <code>:::return</code> clause. You'll need to hang on the pointer because the function arguments are not passed to <code>:::return</code>:
<pre>
syscall::open:entry
{
self->filename = arg0;
}
syscall::open:return
/self->filename/
{
@files[copyinstr(self->filename)] = count();
self->filename = 0;
}
END
{
trunc(@files, 5);
}
</pre><p>
<li> <b>Seeing what time machine is looking at</b> (DTrace->General)<br>
Curious what Time Machine is looking at while doing backups? This one-liner will show all of the files that are being opened:
<p>
<code>sudo dtrace -q -n 'syscall::open*:entry/execname=="backupd"/ { self->name = arg0; }' -n 'syscall::open*:return/execname=="backupd"/ { printf ( "%s opening %s\n
", execname, copyinstr(self->name)) ; self->name = 0}'</code>
<p>
Or in a more readable form:
<pre>
syscall::open*:entry
/execname=="backupd"/
{
self->name = arg0;
}
syscall::open*:return
/execname=="backupd"/
{
printf ( "%s opening %s\n", execname, copyinstr(self->name));
self->name = 0;
}
</pre>
Note that FileValue will cause errors of the kind "invalid user access in action #2 at DIF offset 24". I don't know how to work around that.<p>
<li> <b>So, what's hammering my network / disk?</b> (DTrace->General)<br>
Sometimes your harddrive is cheebling, or maybe your DSL modem lights are lit up like a Christmas Tree. The network activity monitor in Mavericks is beyond useless for figuring out exactly who the guilty party is. DTrace to the rescue. Run this rootstyles:
<pre># <b>dtrace -n 'syscall::read:entry { @read[execname] = sum(arg2); }' -n 'syscall::read_nocancel:entry { @read[execname] = sum(arg2); }'</b>
dtrace: description 'syscall::read:entry ' matched 1 probe
dtrace: description 'syscall::read_nocancel:entry ' matched 1 probe
<b>^C</b>
...
mdworker 27699995
dbfseventsd 37756854
ocspd 125588322
storeagent 431376595
</pre>
Sure enough, it's the MacAppStore program, downloading updates even though it's been told not to.<p>
<li> <b>Timing stuff</b> (DTrace->General)<br>
To time stuff in DTrace, get the timestamp (wall-clock time) or vtimestamp (on-CPU time) in the <code>entry</code>. In the <code>return</code>, make sure you have a starting timestamp recorded (this avoids race conditions if the script is run if the function is currently in-flight). Then calculate the delta and do something with it (print it, aggregate it, whateva).
<pre>
some:probe:description:entry
{
self->start = timestamp;
}
some:probe:description:return
/self->start != 0/
{
this->delta = timestamp - self->start;
trace (this->delta);
self->start = 0;
}
</pre><p>
<li> <b>non-const pointer error</b> (Errors->General)<br>
Got an error that smells like "<code>pointer to non-const type 'GRSpotifyLoginCompletion' (aka 'void (^)(BOOL)') with no explicit ownership</code>"? Check to see you don't have too many stars.
<p>
For example,
<pre>
typedef void (^GRSpotifyLoginCompletion) (BOOL loggedIn);
@property (strong, nonatomic) NSMutableArray *loginCompletions;
...
for (GRSpotifyLoginCompletion *completion in self.loginCompletions) {
completion(success);
}
</pre>
This generates the error. The problem is the star before <code>completion</code> in the fast-enumeration loop. GRSpotifyLoginCompletion is already a pointers due to its nature of being a block typedef. The code above is saying "hey, this completion thing is a pointer to a pointer", which ARC doesn't know how to handle.<p>
<li> <b>Getting a list of supported compiler flags</b> (llvm->General)<br>
<pre>
% <b>clang -cc1 --help</b>
OVERVIEW: LLVM 'Clang' Compiler: http://clang.llvm.org
USAGE: clang -cc1 [options] <inputs>
OPTIONS:
-Eonly Just run preprocessor, no output (for timings)
-E Only run the preprocessor
-F <value> Add directory to framework include search path
-H Show header includes and nesting depth
...
</pre>
Unfortunately it doesn't show all the warning flags.<p>
<li> <b>Automating Pitch Bend</b> (Logic->General)<br>
Pitch bend is a MIDI thing rather than an automatable control, so you have to tweak it via the MIDI Editor (formerly known as HyperDraw):
<ol>
<li> select the region to automate
<lI> show piano roll
<li> turn on MIDI editor
<li> choose "Pitch Bend" from the controller
<li> draw in the foobage with the pencil tool.
</ol>
<img src="/quickies/files/logic-pro-x-pitch-bend.png" width='842' height='712'>
<p>
And <a href="/quickies/files/pitch-auto.mp3"> sounds like this </a>.<p>
<li> <b>Keep the alligator gates open</b> (Reason->General)<br>
Sometimes you want to use the Alligator as a filter, but don't want the gate action kicking in. Here's a way to turn them off that doesn't require a Matrix and a Spider:
<ul>
<li> set pattern ID to #60 <i>(although I haven't seen a difference depending on what pattern is selected)</i>
<li> Turn the Amp Env Decay setting all the way to the right (to 127)
<li> Flip the rack around, and connect the CVs in a round-robin fashion (output 1 to gate 3, output 2 to gate 1, output 3 to gate 2), or look at the illustration.
</ul>
<img src="/quickies/files/alligator-gate-open.png">
<p>
This can be kind of twitchy if you change presets. You may need to break the CV (unhook one connection, the reattach it) to get sound flowing again.
<p>
<i>(taken from <a href="http://www.reason101.net/101-creative-reason-projects/68-all-about-the-alligator/">Reason 101's all about the alligator. Get the dude's <a href="http://www.reason101.net/shop/">ebook</a>, it's awesome.)</i><p>
<li> <b>BNRTimeBlock in Swift</b> (Swift->General)<br>
This <a href="https://www.bignerdranch.com/blog/a-timing-utility/">blog post</a> has a little snippet for timing a block. Here's a Swift version:
<pre>
func BNRTimeBlock(_ block: @noescape () -> Void) -> TimeInterval {
var info = mach_timebase_info()
guard mach_timebase_info(&info) == KERN_SUCCESS else { return -1 }
let start = mach_absolute_time()
block()
let end = mach_absolute_time()
let elapsed = end - start
let nanos = elapsed * UInt64(info.numer) / UInt64(info.denom)
return TimeInterval(nanos) / TimeInterval(NSEC_PER_SEC)
}
</pre>
And call it like
<pre>
let time1 = BNRTimeBlock {
print("groovy")
// do other work.
}
print(" took (time1)")
</pre><p>
<li> <b>Printable description from Swift thingies</b> (Swift->General)<br>
Tired of default output from your objects when doing a sanity-check <code>print</code>?
<p>
Just adopt <code>CustomStringConvertible</code> and implement <code>description</code>:
<pre>
extension Weight: CustomStringConvertible {
var description: String {
return "(pounds) lbs"
}
}
</pre>
If you're inheriting from NSObject, then you actually already have a description method, and will get an error to the tune of "redundant conformance of blah blah blah". In that case, you'd do this:
<pre>
extension IndoorCyclingClass {
override var description: String {
return "(title) - (classDescription)"
}
}
</pre><p>
<li> <b>Swift if case let</b> (Swift->General)<br>
Because I can never remember the syntax:
Given
<pre>
enum FilterOptionValue {
case oneSided(value: Double)
case twoSided(low: Float, high: Double)
}
</pre>
You can unpack it piecewise on demand with
<pre>
if case let .twoSided(low, high) = thingieOption.value {
minThingie = Int(low)
upperThingie = Int(high)
}
</pre><p>
<li> <b>#if 0 in Swift</b> (Swift->Hacks)<br>
Have a bunch of code to temporarily chop out because of reasons? In C you could use <code>#if 0</code> to tear out chunk of your file.
<p>
<code>#if</code> in Swift generally requires the guts of the conditional to be syntactically correct (even if it's semantically nonsense).
<p>
You can abuse the Swift version check to do something similar to <code>#if 0</code>:
<pre>
...
#if swift(>=666)
aj2n42j4n23jnjsdnfjsnfjnunrun unr unwu nudjfn jsnf jn
var window: UIWindow?
#endif
...
</pre>
The compiler still does some processing of the code, so you might get an error (like if that cat-stomp at the beginning started with a digit, Swift tries interpreting 2n42j4n23jnjsdnfjsnfjnunrun as an integer literal and fails)
<p>
<i>(Thanks to Jeremy Sherman for the idea)</i><p>
<li> <b>Doing Notifications Swiftstyle</b> (Swift->Random)<br>
Posting end
<pre>
NotificationCenter.default.post(name: Notification.Name("FishBattery"), object: tour)
</pre>
Receiving end
<pre>
NotificationCenter.default.addObserver(
forName: Notification.Name("FishBattery"),
object: tour,
queue: OperationQueue.main) { [weak self] notification in
guard let tour = notification.object as? Tour else {
return
}
if tour == self?.tour {
self?.uploadIfNeeded()
}
}
</pre><p>
<li> <b>Running swift code in LLDB</b> (Swift->lldb)<br>
<pre>
(lldb) <b>expr -l swift -- let $ook = "verb"</b>
(lldb) <b>expr -l swift -- print("I seem to be a ($ook)")</b>
I seem to be a verb
</pre>
And if you want to call your own stuff (say the project name is <code>C-Interop</code>):
<pre>
(lldb) <b>expr -l swift -- import C_Interop</b>
(lldb) <b>expr -l swift -- SomeClass().useAnother()</b>
</pre>
This creates a new instance of <code>SomeClass</code> and calls the <code>useAnother</code> method.
<p>
(Thanks to Zach Waldowski for this one.)<p>
<li> <b>Instantiate a new view controller from a storyboard</b> (UIKit->General)<br>
<pre>
let detailVC = self.storyboard?.instantiateViewController(withIdentifier: "PlaylistDetailsViewController")
as! PlaylistDetailsViewController
detailVC.playlist = playlist
self.present(detailVC, animated: true, completion: nil)
</pre><p>
<li> <b>Using UIAlertController</b> (UIKit->Swift)<br>
Here's some moving parts for UIAlertController and friends, also does the username/password alert jazz.
<pre>
func showLoginPanel() {
let alertTitle = "Please enter your Equifax username and password"
let message = "We will only use this information for good, not evil."
let alertController = UIAlertController.init(title: alertTitle,
message: message,
preferredStyle: .alert)
alertController.addTextField { textfield in
textfield.placeholder = "Equifax Username"
}
alertController.addTextField { textfield in
textfield.placeholder = "Password"
textfield.isSecureTextEntry = true
}
let stopAction = UIAlertAction(title: "Login",
style: .default)
{ action in
guard let login = alertController.textFields?.first,
let password = alertController.textFields?.last else {
print("This is wholly unexpected")
return
}
print("Do things with: (login.text!)")
}
alertController.addAction(stopAction)
let cancelAction = UIAlertAction(title: "Cancel",
style: .cancel,
handler: nil)
alertController.addAction(cancelAction)
present(alertController, animated: true)
}
</pre><p>
<li> <b>Scroll to a particular collection view item.</b> (UIContainerView->General)<br>
<pre>
- (void) viewDidLayoutSubviews {
// doing this in viewDidAppear might not work well b/c the collection view might
// not have completed layout yet
[super viewDidLayoutSubviews];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem: self.currentPatchNumber
inSection: 0];
UICollectionViewScrollPosition scrollPosition =
UICollectionViewScrollPositionCenteredVertically; // maybe also horizontally if you want
[self.collectionView scrollToItemAtIndexPath: indexPath
atScrollPosition: scrollPosition
animated: NO];
} // viewDidLayoutSubviews
</pre><p>
<li> <b>Adding a new environment value</b> (SwiftUI->Random)<br>
<pre>
private struct ScreamAtMeKey: EnvironmentKey {
static let defaultValue = false
}
// Set via .environent(\.screamAtMe, true)
extension EnvironmentValues {
var screamAtMe: Bool {
get { self[ScreamAtMeKey.self] }
set { self[ScreamAtMeKey.self] = newValue }
}
}
// Set via .screamAtMe(true)
extension View {
func screamAtMe(_ aaaaaah: Bool) -> some View {
environment(.screamAtMe, aaaaaah)
}
}
</pre><p>
<li> <b>Basic Tab View</b> (SwiftUI->Random)<br>
<pre>
struct ContentView: View {
var body: some View {
TabView {
SimplePlan()
.badge(23)
.tabItem {
Label("Simple Plan", systemImage: "house")
}
MultiPlan()
.tabItem {
Label("Simple Plan", systemImage: "house.lodge")
}
}
}
}
</pre><p>
<li> <b>Default to a particular tab bar tab</b> (SwiftUI->Random)<br>
<pre>
enum Tab: Hashable {
case simplePlan
case complicatedPlan
case multiPlan
var id: Self { self }
}
struct ContentView: View {
<b>@State private var tab = Tab.complicatedPlan</b>
var body: some View {
<b>TabView(selection: $tab)</b> {
SimplePlan(captureModel: SimpleCaptureModel.shared)
.tabItem {
Label("Simple Plan", systemImage: "house")
}
<b>.tag(Tab.simplePlan)</b>
CompliPlan(captureModel: CompliCaptureModel.shared)
.tabItem {
Label("Complicated Plan", systemImage: "house.circle.fill")
}
<b>.tag(Tab.complicatedPlan)</b>
MultiPlan()
.tabItem {
Label("Multi Plan", systemImage: "house.lodge")
}
<b>.tag(Tab.multiPlan)</b>
}
}
}
</pre><p>
<li> <b>Easily giving buttons a border/background</b> (SwiftUI->Random)<br>
I still dislike the iOS 7 "here's some random blue text. Really, it's a button". I like buttons to look like buttons. You can add a `.buttonStyle` to a view, and it'll apply the style to al the buttons inside.
Here's the different out of the box styles. You can also make your own custom button style.
<pre>
.buttonStyle(.bordered) // gray filled oval
.buttonStyle(.borderedProminent) // blue filled oval
.buttonStyle(.borderless) // default (I think) - free floating random blue text
.buttonStyle(.plain) // free floating random plain text. that's also a button
</pre><p>
<li> <b>Making a custom button style</b> (SwiftUI->Random)<br>
<pre>
struct CustomButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding()
.background(.purple)
.foregroundStyle(.green)
.clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
.scaleEffect(configuration.isPressed ? 2.0 : 1)
.animation(.easeOut(duration: 0.05), value: configuration.isPressed)
}
}
</pre>
(please don't actually make buttons with this style...)
And then attach it to something (say an individual button, or a top-level view) like
<pre>
.buttonStyle(CustomButtonStyle())
</pre><p>
<li> <b>Making a simple button</b> (SwiftUI->Random)<br>
<pre>
Button("Button Text") {
print("Snorgle")
}
</pre><p>
<li> <b>Wrapping a UIView</b> (SwiftUI->Random)<br>
Given a UIView, add it to SwiftUI via UIViewRepresentable.
<pre>
struct PlaceholderContainerView: UIViewRepresentable {
func makeUIView(context: Context) -> PlaceholderUIView {
return PlaceholderUIView()
}
func updateUIView(_ uIView: PlaceholderUIView, context: Context) {
print("update ui view (context)")
}
}
</pre><p>