emacs
inside of screen
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:
screen -e^Oo emacs
screen -r
$HOME
directory, plus the /Applications directory. There's probably other stuff that should be snarfed (like /Library
), 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":
% sudo mkdir /Volumes/Wikkit/backup
% cd /Users
% sudo hfstar cf - bork | (cd /Volumes/Wikkit/backup; sudo hfstar xf -)
To Restore:
% cd /Users
% sudo mv bork bork-orig
% cd /Volumes/Wikkit/backup
% sudo hfstar cf - bork | (cd /Users; sudo hfstar xf -)
Some folks have had success with psync, but we haven't used it yet.
~/.inputrc
file:
set completion-ignore-case On
Then when you type ls /appli
in the shell you'll get ls /Applications/ like you ought to!
% ~/bin/rename.pl 's/.oop$/.ack/' *.oop
To tack on an extension to all files, do something like
% ~/bin/rename.pl 's/(.*)/$1.jpg/' *
% perl -pi -e 's/\r/\n/g' mealcsv.csv
tr '\r' '\n' < macfile.txt > unixfile.txt
% hdiutil create -ov -imagekey zlib-level=9 -fs HFS+ -format UDZO -scrub -srcfolder /Path/To/The/Goodies fooby.dmg
# svc -d /service/whatever
- Bring the server down
# svc -u /service/whatever
- Start the server up and leave it in keepalive mode.
# svc -o /service/whatever
- Start the server up once. Do not restart it if it stops.
# svc -t /service/whatever
- Stop and immediately restart the server.
# svc -k /service/whatever
- Sends the server a KILL signal. This is like KILL -9. If svc -t fails to fully kill the process, use this option.
(Thanks to the OpenACS documentation)
% cd /Users/bork/Library/Screen\ Savers
or you can surround it with quotes:
% cd "/Users/bork/Library/Screen Savers"
Note that tilde expansion (~/Library
) is suppressed if you use the quotes.
% screen -r There is a screen on: 8395.pts-0.vikki (Multi, attached)You can boot off that other loser by using:
% screen -d 8395.pts-0.vikkiand then doing
screen-r again
% du -sk dir_name
To see how much each of a directory's contents consume, use
% du -sk dir_name/* | sort -n
to get a list ordered by size.
% otool -s __TEXT __info_plist ./thebinary | xxd -r
Muchos Thankos to Dan Jalkut for this one.
% cd directory
% find . -name "*.h" -exec grep NSDoWhatIMean {} ; -print
{}
construct. Also make sure there's a space after the {}
and ;
-h
flag:
% grep -h chicken ~/Documents/ircLogs/FreeNode/2007*/#macsb*
egrep -h -o '@"[^"]*"' MJFirstViewController.mThe -h suppresses printing the file name, -o is "Prints only the matching part of the line"
find /Volumes/Vikki/ -type f -size +2G -print
% sw_vers
ProductName: Mac OS X ProductVersion: 10.3.7 BuildVersion: 7S215
% ps -eo "%p %P %c"
% find . -type f -perm +06000 -print
(or use find /
to start looking from the root directory.)
fcntl
, all things are possible.
char pathbuf[PATH_MAX]; if (fcntl(fd, F_GETPATH, pathbuf) >= 0) { printf ("wow: %s\n", pathbuf); } else { printf ("oops %s\n", strerror(errno)); }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.
% grep -c "fix me" *.m
% curl -O http://borkware.com/quickies/files/fullscreensaver.tar.gz
-l
(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'
% grep -l rzolf *.ham | xargs open
-e
option to the rescue:
% grep -e -H filename
hoover Greeble NI POPand file2.txt had the contents
bork Bork BORKInterleave them with
% paste -d '\n' file1.txt file2.txt > blargand get
hoover bork Greeble Bork NI POP BORK
% hdiutil mount diskimage.dmg
LANG
environment variable to be en_US
to make it happier.
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!"
Unsurprisingly, major OS updates seem to reset this value. (Firmware updates may do it too.)
(This may be incomplete - I have a couple of days still to make sure it totally works)
Turn off hibernation with:
# sudo pmset -a standby 0 # sudo pmset -a hibernatemode 0
% defaults write com.bignerdranch.bigshow NSQuitAlwaysKeepsWindows -bool false(thanks to Step Christopher for this one)
experimental
:
% find . -path ./experimental -prune -o -name "*.swift"
% rm -- -i-am-a-bad-file
% ls -l 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 ...Which are quarantine tags:
% ls -l@ 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 61Get rid of them with this:
% xattr -d com.apple.quarantine *
% perl -pi.bak -e 's/OLDSTRING/NEWSTRING/g' *.html
% find . -type f -name "*.html" -exec grep Spoon {} ; -print
/Volumes/Media2
% rsync -avz -e ssh --progress 'biggun.local:/Volumes/Video/AppleTV\ Videos/Exercise' /Volumes/Media2I can never remember what the flags mean, so:
% sudo sh -c "tar cf - * | (cd /mirror4/web; tar xf -)"
% xmllint -noout kipple.xml
Handy for a quick check after hand-editing a file.
% gcc -v --help
ls -l
:
% ls -l ~/junk/SnapshotRepository.sparseimage -rw-r--r--@ 1 markd markd 135270400 Jul 24 20:38 /Users/markd/junk/SnapshotRepository.sparseimageThat means there's some extended attributes. use
ls -l@ to see them
:
% 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
comm
.
Here I'm wondering if any objc files in the current directory also exist in the Classes subdirectory
% ls -1 *.[hm] *.xib | sort > oopack % cd Classes % ls -1 *.[hm] *.xib | sort > ../oop2 % cd .. % comm -12 oopack oop2The
-12
suppresses lines only in file1, and lines only in file2. I'm more interested in their intersection.
% tail -f /var/tmp/console.log
% unzip -l snorgle.zip
ppid
option with the -o
flag:
% ps -axo user,pid,ppid,vsz,tt,state,start,time,command
% defaults write com.apple.PowerChime ChimeOnNoHardware -bool true % sudo killall PowerChime
So, trick sort into using the last field (with a colon separator) as the sort key:
% grep -c translatesAuto *.xib | sort -t: -n -k2
^t
is the hotkey, doing ^t ^g
will toggle the visible bell on and off.
% cat some-unformatted-manifest.json | python -m json.tool(courtesy of Chris Donnelly)
% find . \( -name "*.jpg" -or -name "*.gif" \) -exec do_something_with {} ;
% find . -name "*.jpg" -exec convert -verbose -geometry 150x150 {} {} ;
% identify filename.jpg
If you want to be studly, you can use -format
to tailor
the output
% identify -format "insert into bw_graphics values (generate_primary_key(), '/images/llamas/%f', %w, %h);" *.jpg
generates a sql insert
statement for each image.
nm
outputs a three-column format. I needed to get the set of defined symbols (in particular those that start with __
, in tracking down a C++ linking problem), which is the third column. awk
is good for that. Use $N
to get the nth column (zero-index). This pipeline did the trick:
nm ./oopack.o | awk '{print $2}' | sort | grep __(Thanks to DougEDoug for the pointer)
% awk '{ for (f=1; f <= NF; f++) { if (f != 1 && f != 9) printf("%s ", $f);} printf(" ");}' < ook.txtOr you can put this at the end of a pipeline instead of directing a text file into the command. (courtesy of DougEDoug)
% tar cf - ./stuff | ssh theOtherMachine "tar xf -"
% cd /Users/bork/development/cool\ stuff/neat\ things
The other is to use shell completion to put in the backslashes for you. In
the above path, if you type
% cd /Users/bork/development/cool[tab]
, it'll expand
"cool stuff
" for you.
cp -R
is generally unsafe to copy directories with since it will copy through symbolic links rather than just copying the link (newer cp
's have an -H flag that will work around this). The classic unix way of doing things like this is a push-pull tar
:
% tar cf - dir-name | (cd /destination-dir; tar xf -)
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 sudo
), the file owners and permissions will be preserved (which cp
won't do)
rf
command to append to an archive rather than creating one:
% find . -name "*.yuck" -print0 | xargs -0 tar rvf oop.tar
-print0
(zero) tells find to output the filenames with trailing NUL characters (rather than newlines), and -0
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.
Thanks to Ben Cox for the -print0 / -0 suggestion.
% ssh-keygen -t rsa
% scp ~/.ssh/id_rsa.pub gitdown.com:
% cat ~/id_rsa.pub >> ~/.ssh/authorized_keys
mv: rename build/ to oopack: Is a directorywhen using
mv
or ln
on the command line, that's a Darwin error. To correct the problem, remove the trailing slash from the directory name.
% sudo tcpdump -Atq -s 0 -i en1
-i en1
will display traffic on your airport card. Use en0
(or nothing, for most systems) for built-in ethernettage.
You can add a host line to limit output to a particular host
% sudo tcpdump -Atq -s 0 -i en1 host zombo.com(thanks to Dan Jalkut for this one)
% launchctl stop com.apple.Dock.agent % launchctl start com.apple.Dock.agent
// Boot into single-user mode with command-S on power-on # /sbin/fsck -fy # /sbin/mount -uw / # mv /var/folders /var/folders-dorked # rebootNearly instant boot again.
touch
. You can specify a specific date or time using the format [[CC]YY]MMDDhhmm[.SS]]
. Here's an example with alternating bolds to make it easier to read:
% touch -t 200703141536 snork.waffle % ls -l snork.waffle -rw-r--r-- 1 markd markd 298 Mar 14 15:36 snork.waffle
% hdid ram://size
where size is the size in bytes. the system will round that number up or down as it sees fit.
% sw_vers -productVersion 10.5.1
/usr/bin/drutil info
to get a listing of the kinds of media your drive supports. For example, on the 17" iLamp:% drutil info 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
chflags
in Mac OS X works like chattr
over in Linux-land. So to make a file immutable (can't change it, even if you're the superuser), you can sudo chflags uchg file-name(s)
. To undo it, do sudo chflags nouchg file-name(s)
# niutil -create . /machines/tower # niutil -createprop . /machines/tower ip_address 10.0.1.155 # niutil niutil -createprop . /machines/tower serves ./local
/System/Library/StartupItems/SystemTuning/SystemTuning
tweak some values, like
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 megsSave, and restart your system.
On some Panther seeds, you may need to add these things to /etc/rc
/etc/rc
, 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 /etc/rc
change yesterday, it doesn't look any different?" Here's how:
% sudo mv rc rc-orig
% sudo cp rc-orig rc
% sudo vi rc
% sudo mv rc-orig rc
Thereby undoing your changes, and not messing up the timestamp of the original file.
(mucho thankos to Louis Bertrand for this one)
cc1obj: error: type '<built-in>' does not have a known size
(void)
, a typo that should have been (void *)
.
mach_absolute_time
is the finest-grained timer in the system. Here's a little utility to time a block:
#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; } // BWTimeBlockAnd you would use it like:
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);
@executable_path/../blah
. Sometimes you get a library from someone else (or something generated by a gigantic configure script) and need to change the install name. The install_name_tool
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:
% install_name_tool -id @executable_path/libbork.dylib ./libbork.dylib
In Interface Builder 2: 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.
In Interface Builder 3: 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.
(muchos thankos to Quinn Taylor at the BYU CocoaHeads for the IB3 update)
% gcc -E -dM -x c /dev/null
(thanks to xmath on #macdev for this one)
% otool -L /path/to/application.app/Contents/MacOS/application
The problem is, if I include the usefulStuff.c
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 usefulStuff.c
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.
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).
In the source file for the plugin that uses stuff from usefulStuff:
Declare the function to be weak:
extern void BWDebugLog (int blah, char *blah) __attribute__((weak_import));(you can also do this in your header files. In this case, I didn't want to touch the header)
Before you use the function, make sure it's not NULL
. Note there's no trailing ()
after the function name.
if (BWDebugLog != NULL) { BWDebugLog (23, "stuff"); }In the Xcode project for the plugin
Add the flags -flat_namespace
and -undefined dynamic_lookup
to the "Other Linker Flags" (a.k.a. OTHER_LDFLAGS
). 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.
In the Xcode project for the test harness
Add usefulStuff.c
, and turn on the "Preserve Private External Symbols" checkbox (a.k.a -keep_private_externs
). That turns the symbol BWDebugLog
(and others) into exported symbols that'll be visible to the plugin. Otherwise the plugin will never get past that != NULL
check earlier.
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.
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.
You can force otest to run a particular architecture with the arch command:
% arch -arch i386 /Developer/Tools/otest build/Debug/Snoogle.octest
BWNagType.h:23: `BWConcreteType' defined as wrong kind of tag
In this particular case, I had a type I had declared with:
@class BWConcreteType;
and later on I had decided to turn it into an enum:
typedef enum BWConcreteType { ... } BWConcreteType;
without removing the previous @class
declaration. So be on the look out for conflicting types for the same symbol.
xcodebuild
. We've got a rant about cocoa development in emacs, which includes a discussion of pbxbuild
, the predecessor to xcodebuild
. Some of the arguments have changed over the years. I usually run it like: % xcodebuild -configuration "Debug" -target "My Groovy App"If you have one target, or have a default target, you can leave out the
-target
argument.
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".
- (BOOL) canPerformAction: (SEL) action withSender: (id) sender { return action == @selector(duplicate:); } // canPerform - (BOOL) canBecomeFirstResponder { return YES; } // canBecomeFirstResponerThen 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:
// 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];If you have work to do when the menu disappears (such as deselecting a tableview cell), you can listen for
UIMenuControllerWillHideMenuNotification
or one of its kin.
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)
@interface FSContinousTextField : NSTextField @end @implementation FSContinousTextField - (void) textDidChange: (NSNotification *) aNotification { [[self target] performSelector:[self action] withObject:self]; } @end
#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);(Thanks to TVL for helping me figure this one out)
CFUUIDRef cfuuid = CFUUIDCreate (kCFAllocatorDefault); NSString *uuid = (NSString *)CFUUIDCreateString (kCFAllocatorDefault, cfuuid); CFRelease (cfuuid);Because a core foundation "Create" function is called, you're responsible for releasing
uuid
when you're done with it.
% defaults write com.borkware.snongflozzle ookook -array thing1 thing2 thing3
[self performSelector: @selector(whatever) withObject: nil afterDelay: 0];
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
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; gridType = [defaults objectForKey: @"gridType"];Will have the value of "flarn" whether you do something like
% defaults write com.borkware.BorkStitch gridType flarnor
% ./build/Debug/BorkStitch.app/Contents/MacOS/BorkStitch -gridType flarn
-fobjc-arc
flag.
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-align" scan = (fileinfo *) (((char *) scan) + scan->length); #pragma clang diagnostic pop
void giveSomeLove () { // give the app some love so it'll update the window [[NSRunLoop currentRunLoop] runUntilDate: [NSDate distantPast]]; } // giveSomeLove
unsigned index; for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index]) { ... }(courtesy of mikeash)
if ([NSBundle loadNibNamed: @"theNibName.nib" owner: self]) { ... life is happy } else { ... couldn't load the nib }
NSRect selectionRect = ...; NSValue *value; value = [NSValue valueWithRect: selectionRect];
% find . -name "*.h" -print -exec cat {} \; > ~/moby-file.h
void *tempCopyOf(void *data, NSUInteger size) { return data ? [[NSData dataWithBytes:data length:size] mutableBytes] : NULL; }OBTW, these techniques will fail under GC. Here's how you'd write a working one under GC:
void *tempCopyOf(void *data, NSUInteger size) { void *buffer = NULL; if( data ) { buffer = NSAllocateCollectable(size, 0); memcpy(buffer, data, size); } return buffer; }Unfortunately I'm not aware of a nice way that works in dual-mode code.
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.
NSString *filename = @"/this/is/my/file/path"; NSData *data; data = [NSData dataWithContentsOfFile: filename];
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; [defs removePersistentDomainForName: appDomain];
UIStatusBarStyle
to UIStatusBarStyleOpaqueBlack
NSUInteger index = indexPath.row;
if ([_selection containsIndex: index]) [_selection removeIndex: index];
else [_selection addIndex: index];
defaults write -g ApplePressAndHoldEnabled -bool false(thanks to Paul Kafasis)
defaults write NSGlobalDomain NSAutomaticWindowAnimationsEnabled -bool NO
% defaults write -globalDomain NSUseAnimatedFocusRing -bool NOThanks to @rsesek.
printf ("\u3232_\u3232\u2122\n");
In this case, the selector being called takes two arguments, one of which is an object, the other is an NSTimeInterval
. The atIndex:
jazz starts with 2 so that the self parameter and the selector can be passed to the method.
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];
- (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
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];and the didEndSelector implementation looks kinda like this:
- (void) openPanelDidEnd: (NSOpenPanel *) sheet returnCode: (int) returnCode contextInfo: (void *) context { if (returnCode == NSOKButton) { NSArray *fileNames = [sheet filenames]; NSLog (@"wooOOooot! %@", [fileNames objectAtIndex: 0]); } } // openPanelDidEnd
Localizable.string
, your NSLocalizedString()
call will not look up the strings in the file. To verify the file, do something like this:
% pl < Localizable.strings
% gcc -E -dM - </dev/null
// 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
# pmset lidwake 0
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); }
% /Developer/Tools/DeRez -useDF ./blarg.rsrc /Developer/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon/MacTypes.r > oopack2And the reverse process is like:
% /Developer/Tools/Rez -o blah.rsrc -useDF /Developer/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon/MacTypes.r oopack2
% cat /System/Library/Frameworks/Foundation.framework/Headers/*.h > ~/moby % cat /System/Library/Frameworks/AppKit.framework/Headers/*.h >> ~/moby % chmod 444 ~/moby
- (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); } // snorgWaffleand can be called like
[self snorgWaffle:@"red", @"planet", @"zeitgeist", nil];
printf ("hello %.*s\n", 5, "there is no good going on"); NSLog (@"hello %.*s\n", 5, "there is no good going on");results in
hello there 2007-05-04 09:14:15.575 printf[4914] hello there
Similarly, something like printf ("hello %*.*s\n", 10, 5, "florgsnorgle");
will right-justify 'florg' in a field of 10 characters.
(Thanks to TVL for this goodie)
class Bargle { static func greeble() { print("Blargle greeble") } func oopack() { type(of: self).greeble() } } let bargle = Bargle() bargle.oopack()This will cause Blargle greeble
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)
struct Thingie { let blah: Uint8 let greeble: UInt8 let splunge: UInt8 }Get the sizeof via
let packetLength = MemoryLayoutThat's the packed size. To account for alignment/padding, use stride..size
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()))(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)
gdb
There are two ways to enable core files:
% limit coredumpsize unlimited
.cshrc
. I don't know what to do for bash
)
struct rlimit corelimit = { RLIM_INFINITY, RLIM_INFINITY };
setrlimit (RLIMIT_CORE, &corelimit);
You may need to also add#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
Core files seem to be getting dumped into /cores
, rather than the directory the program was run from.
/System/Library/Frameworks/Foundation.framework/Headers/NSDebug.h
. Lots of low-level unsupported APIs for mucking around with gory technical details.
Put this somewhere convenient to trigger - in a timer, under a button, or something. Obviously, you don't want to ship with it.
[[UIApplication sharedApplication] performSelector:@selector(_receivedMemoryNotification)];
% defaults write com.apple.CrashReporter DialogType none
Turn turn it back on, replace none
with prompt
to get the original behavior. See for details.
fprintf
% gdb build/Debug/Snorkfizzle.app/Contents/MacOS/Snorkfizzle (gdb) set env NSZombieEnabled=YES (gdb) fb -[_NSZombie methodSignatureForSelector:] (gdb) runThen exercise your app and trip the error.
(gdb) thread apply all where
(gdb) fb -[NSTextView drawRect:]
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 endAnd you'll get some output like this:
-[NSTableColumn _bindingAdaptor] +[NSBinder binderClassesForObject:] +[NSBinder _allBinderClasses] +[NSDisplayPatternTitleBinder isUsableWithObject:] +[NSBox self](courtesy of Rob Mayoff)
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 debugWhich is fine and dandy, but it lies. I've never gotten
szone_error
to actually do anything. Try breaking on malloc_printf
instead.
2007-05-05 17:18:00.702 QueenStitcher[2804:117] *** Assertion failure in -[NSColorWell setColor:], NSColorWell.m:497, u suk l0s3r
, and then the runloop happily runs again, giving you no clue where the problem is. I tell gdb
to always break on Cocoa exceptions:
fb -[NSException raise] fb objc_exception_throw()For maximal enjoyment, add these two lines to your
~/.gdbinit
file, so they'll get set no matter how you invoke gdb
(no need to add these to every single project, for instance).
I've been told VoiceOver uses exceptions heavily, so if you're doing VoiceOver development, these breaks may cause you pain.
'bork'
. You can have gdb print them out if you need to look at one or two of them:
(gdb) print/T 1936746868 $4 = 'spit'(thanks to Daniel Jalkut for the print/T trick)
(gdb) po *(int*)($ebp+8)
(gdb) x /80xb attrBufferwill give you something like
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
(gdb) handle SIGTRAP nostop
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.
The attach
command's -waitfor
option to make gdb lie in wait for a launching process:
% gdb /some/app/blah (gdb) attach -waitfor blahNow when a
blah
process launches, gdb will stop it and attach to it.
$r3
and go up from there. For Objective-C method sends, $r3
has 'self', and $r4
has the name of the method. Subsequent arguments are in $5
and so on.
(gdb) print (char*) $r4 $5 = 0x90874160 "drawRect:" (gdb) po $r5 <BWStitchView: 0x1a6670>
(gdb) print (int)[theObject retainCount]
If you're expecting to have an excessively high number of retains, you can use (unsigned int)
in the cast. I find (int)
a skootch faster to type.
.gdbinit
and then you can use wchar_print
: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
info selectors
will show you all of the selectors in the application's symbol table. info functions
will show you all of the functions. You can supply regular expressions to limit the output.
libgmalloc
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 libgmalloc
in gdb, do this:
(gdb) set env DYLD_INSERT_LIBRARIES /usr/lib/libgmalloc.dylib
(gdb) call (void)[textField setStringValue: @"Bork"]
% gdb /Developer/Tools/otest 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.You can supply a -arch flag to pick what you want:
% gdb -arch i386 /Developer/Tools/otestAnd then debug your 32-bit unit test.
(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
.emacs
file:(global-set-key "C-Z" nil)
Copy into register 1:
Paste from register 1:
View the contents of a register
Register names can be single letters, numbers, characters. Upper and lower case are distinct.
#define DONT_REOPEN_PTY
(setq c-default-style "bsd" c-basic-offset 4)
deletechar
into backward-delete-char-untabify
causes backspace in incremental search to cancel the search, which is annoying.
One option is to set the TERM env var to rxvt:
% setenv TERM rxvtBefore cranking up screen.
C-x (
: start recording keyboard macroC-x )
: stop recording keyboard macroC-x e
: replay current keyboard macro
.emacs
file. This will make spaces the indent character, and use 4 spaces per indent level, for C, C++, and Objective C:
(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))))
.emacs(add-to-list 'auto-mode-alist '("\\.h$" . objc-mode))
- Rectangular cut and paste (emacs->General)
[permalink]
- Move to top-left of source rectangle.
C-SPC
to set the mark
- Move to bottom-right of source rectangle
-
M-x kill-rectangle
- Move to paste destination
-
M-x yank-rectangle
- Resetting shell mode's idea of the current working directory (emacs->General)
[permalink]
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 !$
). M-x dirs
will tell the shell buffer to figure out what the current working directory is.
- Restrict editing to the region (emacs->General)
[permalink]
M-x narrow-to-region
Hides everything not in the current region.
- Revisiting / reloading a file in emacs (emacs->General)
[permalink]
The $Id: $
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 M-x revert-bufer
or bind that to a key, or else use a trick by doing C-x C-v
which invokes find-alternate-file
, but just so happens to have the current buffer name, so you just have to do C-x C-v RET
- Running shell command pasting result back into the buffer (emacs->General)
[permalink]
So to run uuidgen
, for instance:
C-U M-!
ret uuidgen
ret
- Scroll line with cursor to the top of the window (emacs->General)
[permalink]
C-U 0 C-L
(you can put in another number besides zero to scroll the line with the cursor to that particular line in the buffer)
- Setting variables when loading a file (emacs->General)
[permalink]
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:
/* For the emacs weenies in the crowd.
Local Variables:
c-basic-offset: 2
End:
*/
- Showing current column position (emacs->General)
[permalink]
M-x column-number-mode
- Sort case-insensitive (emacs->General)
[permalink]
M-x sort-lines
uses a case-sensitive search. When that sucks, you can tell emacs to be case insensitive (globally) by doing M-x set-variable RET sort-fold-case RET t
- Toggling read-only mode in a buffer (emacs->General)
[permalink]
C-X C-Q
- Turning off command highlighting in shell mode (emacs->General)
[permalink]
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:
(setq comint-highlight-input nil)
- Turning off font-lock mode everywhere (emacs->General)
[permalink]
(global-font-lock-mode -1)
- Turning off incremental-search highlighting (emacs->General)
[permalink]
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:
(setq search-highlight nil)
You may also need to
(setq isearch-lazy-highlight nil)
To turn off underlining of matching results. Only some OS X installs need this setting.
- Turning off scroll-to-end in shell-mode (emacs->General)
[permalink]
(setq comint-scroll-show-maximum-output nil)
- Undo within a given region (emacs->General)
[permalink]
C-U C-_
- Unnarrowing the region (emacs->General)
[permalink]
M-x widen
- Use only spaces when indenting code (emacs->General)
[permalink]
(setq indent-tabs-mode nil)
- Using carriage returns in query-replace / replace-string (emacs->General)
[permalink]
Use C-Q C-J
(control-Q control-J) each time you want to include a carriage return. e.g. to double-space everything
M-x replace-string RET C-Q C-J RET C-Q C-J C-Q C-J RET
Or to put "bloogie " at the beginning of every line
M-x replace-string RET C-Q C-J RET C-Q C-J b l o o g i e SPACE RET
- compiling emacs .el files (emacs->General)
[permalink]
Big emacs .el
files take a long time to load.
You can compile them into .elc
files by using:
% emacs -batch -f batch-byte-compile filename.el
- emacs registers (emacs->General)
[permalink]
Stick something into a register:
(select stuff)
C-x r x 1
where "1" is the register identifier.
Getting stuff out of a register:
C-x r g 1
- move to previous buffer C-x p (emacs->Hacks)
[permalink]
(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))
- Balance a tag in SGML mode (emacs->Random)
[permalink]
C-/
- Doing Sheets (NSWindow->General)
[permalink]
To show a sheet in a window, use NSApplication to kick it off:
[NSApp beginSheet: saveSheet
modalForWindow: window
modalDelegate: self
didEndSelector: @selector(saveSheetDidEnd:returnCode:contextInfo:)
contextInfo: NULL];
In the controls in the sheet, use something like
[NSApp endSheet: saveSheet returnCode: NSOKButton];
To invoke the didEndSelector
. Inside of that method, you can check the return code and decide what to do:
- (void) saveSheetDidEnd: (NSWindow *) sheet
returnCode: (int) returnCode
contextInfo: (void *) contextInfo
{
if (returnCode == NSOKButton) {
// ...
} else if (returnCode == NSCancelButton) {
// ...
} else {
// ...
}
[sheet close];
} // saveSheetDidEnd
- Finding the height of the title bar (NSWindow->General)
[permalink]
When you setFrame:
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:
- (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
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.
- Force an NSWindowController's nib to be loaded (NSWindow->General)
[permalink]
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 window
method, that will force the nib file to be loaded. Something like this:
+ (BWInspector *) sharedInspector
{
static BWInspector *s_inspector;
if (s_inspector == nil) {
s_inspector = [[BWInspector alloc]
initWithWindowNibName: @"BWInspector"];
assert (s_inspector != nil);
// force loading of nib
(void) [s_inspector window];
}
return (s_inspector);
} // sharedInspector
- Help! My Sheet won't go away (NSWindow->General)
[permalink]
You may need to call [sheet close]
in addition to your [NSApp endSheet: sheet returnCode:23]
- Making a floating palette (NSWindow->General)
[permalink]
To make a floating palette with small controls, make the palette an NSPanel, and n IB make it a "Utility Window".
- Making a panel not eat the key (NSWindow->General)
[permalink]
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.
- Preventing NSPanels from hiding on program deactivation (NSWindow->General)
[permalink]
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
[panel setHidesOnDeactivate: NO];
- Resizing a window with animation (NSWindow->General)
[permalink]
Use setFrame:display:animate:
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:
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];
- Resizing a window with animation and cool cross-fade effect with views (NSWindow->General)
[permalink]
NSViewAnimation
lets you resize a window and cross-fade some views in one operation. (note I've had problems 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)
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];
- Running a window modally (NSWindow->General)
[permalink]
// I am the very modal of a modern major general.
int blah = [NSApp runModalForWindow:[self window]];
and then elsewhere, like in your OK or cancel button
[NSApp stopModalWithCode:NSOKButton];
- Using an NSWIndowController to load a window (NSWindow->General)
[permalink]
Here's how I like doing things. First make a subclass of NSWindowController
:
@interface BWInspector : NSWindowController
{
// ivars, IBOutlets, etc
}
// IBActions, etc
+ (BWInspector *) sharedInspector;
@end // BWInspector
Then make a nib file with the window, the controls, and other fun stuff you want. This one lives in English.lproj/BWInspector.nib
Then in that class method load the window:
+ (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
That will load the nib file, set up the connections and then open the window for display
- I want my window's background to be yellow (NSWindow->Hacks)
[permalink]
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.
@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
I have no idea how you colorize the titlebar.
- Forwarding key events to another window (NSWindow->Random)
[permalink]
To forward key events to another window, make subclass of NSWindow
and override sendEvent
:
- (void) sendEvent: (NSEvent *) anEvent
{
if ([anEvent type] == NSKeyDown) {
[someOtherWindow sendEvent: anEvent];
} else {
[super sendEvent: anEvent];
}
} // sendEvent
It's up to you to figure out what "someOtherWindow
" is, whether it's a delegate or an instance variable that holds the other window.
- Disabling the "autorepeat pops up accent menu" (Hacks->General)
[permalink]
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 defaults write
and relaunch your apps.
% defaults write -g ApplePressAndHoldEnabled -bool false
- Making naked memory autoreleased (Hacks->General)
[permalink]
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.
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);
}
You can get really fancy and assign buffer
to calloc(1, size)
if you want to give it "pass NULL to get a zero-filled buffer back" semantics.
Also note this doesn't play well with garbage collection - caveat nerdor. (Thanks to Ken Ferry for that note)
- Posing as a class (Hacks->General)
[permalink]
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 poseAsClass:
to hook yourself into the matrix during the +load
method:
@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
- Turning off screen dimming / locking (Hacks->General)
[permalink]
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.
The UIApplication itemTimerDisabled
controls whether the screen should dim. Disable it by enabling it:
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];
or equivalently
[UIApplication sharedApplication].idleTimerDisabled = YES;
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:
[[UIApplication sharedApplication] setIdleTimerDisabled: NO];
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];
- Making the VPN survive fast user switching. (Hacks->Hacks)
[permalink]
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.
Open up Internet Connect, select your VPN icon in the toolbar, and choose options from the Connect menu. Uncheck "Disconnect when switching user accounts".
- Exposing documents directory to Files / Finder (Hacks->Random)
[permalink]
Set these both to YES in the Info.plist
UIFileSharingEnabled
LSSupportsOpeningDocumentsInPlace
- Stopping a chirping G5 (Hacks->Random)
[permalink]
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.
- Checking for modifier keys (NSView->General)
[permalink]
In your mouseDown:
if ([event modifierFlags] & NSShiftKeyMask) {
constrain = YES;
}
If you need to check them outside of your mouseDown, and if you're on 10.6 or beyond, you can use +[NSEvent modifierFlags];
If you're stuck in 10.5 or older, you need to do something like this:
dip into carbon:
- GetCurrentEventKeyModifiers returns the modifiers of the last event processed by the event dispatcher. This is usually sufficient.
- GetCurrentKeyModifiers returns the hardware state of the modifiers.
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)
(Thanks to Mike Ash for pointing out the new 10.6 API)
- Converting an event to view coordinates (NSView->General)
[permalink]
NSPoint point = [self convertPoint: [event locationInWindow] fromView: nil];
- Detecting arrow keys (NSView->General)
[permalink]
- (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];
} ... and look at whatever other keys you want
}
- Finding a subview based on tag (NSView->General)
[permalink]
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 NSView
's viewWithTag:
to find it.
- Grabbing a view into an image (NSView->General)
[permalink]
In your NSView subclass:
[self lockFocus];
NSBitmapImageRep *bits;
bits = [[NSBitmapImageRep alloc]
initWithFocusedViewRect: [self bounds]];
[self unlockFocus];
Then you can add it to an NSImage
or save it to a file, or whatever.
If you want to retain the vectoritude of the drawing, you can use [self dataWithPDFInsideRect: [self bounds]]
, and then make an NSPDFImageRep
. Avoid the NSEPSImageRep
since it makes a trip through NSPDFImageRep
along with a slow PS to PDF conversion. (Thanks to Peter Hosey for the PDF hint)
- Making a custom event tracking runloop (NSView->General)
[permalink]
Sometimes in your mouse tracking code you want to use the NSEventTrackingRunLoopMode
, such as you wanting to use an NSNotificationQueue
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
- (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
(and thanks to Rainer Brockerhof for pointing out a no-op line of code from the original version of this)
- Performing relative (grab-hand) scrolling (NSView->General)
[permalink]
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)
@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
You can be fancy and check for the option key by look at [event modifierFlags]
and looking for NSAlternateKeyMask
, and also use the NSCursor open/closedHandCursor.
- Reacting to the Escape key (NSView->General)
[permalink]
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:
- (void) keyDown: (NSEvent *) event
{
NSString *chars = [event characters];
unichar character = [chars characterAtIndex: 0];
if (character == 27) {
NSLog (@"ESCAPE!");
}
} // keyDown
- Reacting to the delete key (NSView->General)
[permalink]
Look for NSDeleteCharacter
in the event's character string:
- (void) keyDown: (NSEvent *) event
{
NSString *chars = [event characters];
unichar character = [chars characterAtIndex: 0];
if (character == NSDeleteCharacter) {
NSLog (@"Delete!");
}
} // keyDown
- Responding to modifier keys only (NSView->General)
[permalink]
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 flagsChanged: (NSEvent *) event
and look at the event there. (flagsChanged: comes from NSResponder, so any responder in the responder chain can react to it)
- Setting a cursor for a view (NSView->General)
[permalink]
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 -[NSView setCursor:]
call (drat!) You can get something similar by adding this to your NSView subclass:
- (void) resetCursorRects
{
[super resetCursorRects];
[self addCursorRect: [self bounds]
cursor: [NSCursor crosshairCursor]];
} // resetCursorRects
- Outlining every view (NSView->Debugging)
[permalink]
You can set the NSShowAllViews default value to have every view outlined. Nested views get outlined in different colors.
Set it "permanently" via
% defaults write com.apple.TextEdit NSShowAllViews YES
(or whatever your application identifier is), or use the one-shot version:
/Developer/Applications/Xcode.app/Contents/MacOS/Xcode -NSShowAllViews YES
- Becoming firstResponder if you don't have an editable cell (NSView->Hacks)
[permalink]
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 not the first responder, instead some lame-O textfield has snagged keyboard focus. To address this, override this undocumented method:
- (BOOL) _hasEditableCell
{
return (YES);
} // _hasEditableCell
This workaround is necessary at least for Mac OS X 10.1.*.
- Implementing -isFlipped in Swift (NSView->Swift)
[permalink]
Trying to do an override or other methody way of isFlipped results in the usual inscrutible Swift error messages. Do it this way instead:
override var flipped: Bool {
return true
}
- Access environment variables from Cocoa (Random->General)
[permalink]
Check out NSProcessInfo's environment
method
- Adding Growl Notifications (Random->General)
[permalink]
It's pretty easy adding growl notifications to your app. Download the SDK from growl.info. Set up your Xcode project to copy the Growl.framework
into your application bundle.
Pick a class to be the contact point with Growl. Your AppController
class is a good place. Import <Growl/Growl.h>
Set the delegate to the GrowlApplicationBridge
:
[GrowlApplicationBridge setGrowlDelegate: self];
Doing this will eventually have the registrationDictionaryForGrowl
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:
- (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
And use this to post a notification:
[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];
Consult the SDK documentation for more explanations of the features, but they are pretty self-explanitory.
- Converting a Fixed to a float (Random->General)
[permalink]
float blah = FixedToFloat(someFixedvalue);
other goodies in FixMath.h
- Determining current localization you're running in. (Random->General)
[permalink]
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)
NSString *languageused = [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex: 0];
(thanks to Glenn Fawcett for this one!)
- Disabling Chimera's url completion pop-up (Random->General)
[permalink]
Add this to your ~/Library/Application Support/Chimera/Profiles/default/xyz.slt/prefs.js
file:
user_pref("browser.urlbar.autocomplete.enabled", false);
- Duration of a movie file (Random->General)
[permalink]
Got some movie files on disk living at a particular URL? Need to know the duration of the video?
- (NSTimeInterval) timeOfVideoAtURL: (NSURL *) url {
AVPlayerItem *movieItem = [AVPlayerItem playerItemWithURL: url];
CMTime duration = movieItem.duration;
Float64 seconds = CMTimeGetSeconds (duration);
return (NSTimeInterval)seconds;
} // timeOfVideoAtURL
- Easy NSLogging of basic data structures (Random->General)
[permalink]
NSString
has a couple of convenience functions for print out basic structures like NSRect
and NSSize
:
-
NSStringFromRect (someNSRect);
-
NSStringFromPoint (someNSPoint);
-
NSStringFromSize (someNSSize);
- Finding things like ~/Library, and ~/Library/Application Services (Random->General)
[permalink]
NSSearchPathForDirectoriesInDomains
is how you find the location of things like Library directories, or User directories, document directory, and the like (this is the NSSearchPathDirectory
). The NSSearchPathDomainMask
is what domains to find things in. For instance for a NSLibraryDirectory
, a NSUserDomainMask
will give you the path to ~/Library
, NSSystemDomainMask
will give you the path to /System/Library
, and so on.
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 ~/Library/Application Support/Borkware
, you can construct it like
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"];
- Getting the current user's name (Random->General)
[permalink]
NSUserName()
or NSFullUserName()
.
Thanks to Peter Hosey for letting us know about a better API for getting these.
- Putting an image on a disk image's window (Random->General)
[permalink]
To have snazzy disk images that have your logo or picture of your dog appear when you open them, do this:
- run disk copy, make a new blank image. I'm calling mine "thingie"
- copy image file to the mounted fake drive (
logo.jpg
)
- open fake drive, choose Show View Options. Make sure you're just changing the current window, and not Everything.
- Choose "picture", select the
logo.jpg
that's on the thingie drive
- resize your window as appropriate
- run this in the terminal to hide the
logo.jpg
file
/Developer/Tools/SetFile -a V /Volumes/thingie/logo.jpg
- eject thingie and re-mount it
- you should now see your image, and no visible picture file icon
- Reducing the size of a .dmg disk image (Random->General)
[permalink]
DiskCopy won't let you create a disk image of less than 5 megabytes (a bit inconvenient for packaging a program that's 30K). Image->Convert Image
will let you compress the image. As a side-effect of the compressed format, this makes the image read-only.
- Reducing verbiage on web pages to make them readable. (Random->General)
[permalink]
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:
- Select all the text
- Go to the services menu and select summarization service
- You'll get a window with a slider at the bottom. Move that down all the way
to the left
- Amazingly, with 9/10 of blog posts it works REALLY well.
(thanks to Chris Forsythe for this one)
- Seeing unread email in your gmail inbox (Random->General)
[permalink]
To see unread stuff in your gmail inbox search for this:
in:inbox is:unread
- Setting environment variables for the gui login session (Random->General)
[permalink]
For environment variables you want set for the user's GUI login session, make a plist called ~/.MacOSX/environment.plist
. Make the root a Dictionary, and add the key/value pairs (all strings) to it. Don't forget to logout and back in.
- Turning off Spotlight (Random->General)
[permalink]
% sudo mdutil -i off /
- minimal tool (Random->General)
[permalink]
I got tired of looking for this everytime I make a simple one-off tool without using Project Builder:
#import
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[pool release];
} // main
And you can compile it on the command line with:
cc -o test -framework Foundation file-name.m
- Disabling smooth scrolling in mountain lion (Random->Hacks)
[permalink]
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):
% defaults write -g NSScrollAnimationEnabled -bool NO
- Extracting PDF text on the cheap (Random->Hacks)
[permalink]
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:
% /usr/bin/mdimport -d2 ../Book.pdf >& oopack.txt
And edit out the little bit of extra metadata output.
- Control + trackpad zoom in Lion (Random->Random)
[permalink]
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
- Fixing strange duplicate symbols (Random->Random)
[permalink]
While compiling OpenSP, the SGML parser for OpenJade, I got these errors:
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)
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
Text::~Text() { }
and in Text.h, add
~Text();
to the Text class.
(I got it to compile and link, so I didn't go back to check to see if just sticking ~Text() {}
in Text.h would work.)
- Fixing strange undefined link symbols (Random->Random)
[permalink]
While building OpenJade, I got these link errors:
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
The link line needs to have "-lstdc++" added to the end. For OpenJade, edit the libtool script and add -lstdc++
to the end archive_cmds
line. (but leave it inside the last closing double-quote)
- Launch Services admin tool (Random->Random)
[permalink]
If you need to poke around Lunch Launch Services, use the lsregister
tool that lives in /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support
- Make Mail.app display plain text messages (Random->Random)
[permalink]
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:
% defaults write com.apple.mail PreferPlainText -bool TRUE
- Putting an imac to sleep at the login box (Random->Random)
[permalink]
Press the power button.
- Sending email with .mac when port 25 is blocked (Random->Random)
[permalink]
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.
- Where to put files for the system-wide web server (Random->Random)
[permalink]
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:
/Library/WebServer/Documents
- Easier Development of Screen Savers (Screen Savers->General)
[permalink]
When you're writing a screen saver, do you find yourself often copying the basted thing to your ~/Library/Screen Savers
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.
% cd ~/Library/Screen Savers
% ln -s ~/Projects/MonkeySaver/build/Debug/MonkeySaver.saver .
- Finding my resources (Screen Savers->General)
[permalink]
To find resources (say an image) from within the screensaver, use NSBundle bundleForClass. So, to get atomsymbol.jpg you would do something like
NSBundle *bundle;
NSString *path;
bundle = [NSBundle bundleForClass: [self class]];
path = [bundle pathForResource: @"atomsymbol" ofType: @"jpg"];
image = [[NSImage alloc] initWithContentsOfFile: path];
- How about a simple sample or two? (Screen Savers->General)
[permalink]
There's SimpleSaver, a minimal 'bounce a line around the screen' screensaver. FullScreenSaver is the same thing, but it bounces the line around the user's desktop instead of just a black screen. Also check out Drip, a little more sophisticated (even has a configuration panel).
- How do I set the name that's displayed in the preference panel? (Screen Savers->General)
[permalink]
The "Product Name" setting on the Project Builder target has that. Change it by:
- open the
Targets
pane in Project Builder
- double-click on your target
- go to the
Build Settings
tab
- Edit your
Product Name
under General Settings
- Where is my preference file? (Screen Savers->General)
[permalink]
The screen saver preference file lives in the
~/Library/Preferences/ByHost
directory. The name of the file is the name that you pass to defaultsForModuleWithName:
, followed by
.someBigNumber.plist
.
So, on my machine,
userPrefs = [ScreenSaverDefaults defaultsForModuleWithName: @"BWDrip"];
creates a file
/Users/bork/Library/Preferences/ByHost/BWDrip.0003931024a6.plist
- Running a screensaver as your desktop background (Screen Savers->Random)
[permalink]
In a terminal, run
% /System/Library/Frameworks/ScreenSaver.framework/Resources/ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine -background &
(all one command)
- Basics (NSTimer->General)
[permalink]
- (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];
- NSTimers and NSTerminateLater (NSTimer->General)
[permalink]
If your application has an open TCP connection to a server and you receive an applicationShouldTerminate:
message, you're likely to want to returnNSTerminateLater
so that you can first gracefully shut down the connection before quitting.
There is a gotcha: returning NSTerminateLater
stops the main runloop, so your NSTimers
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 NSTerminateCancel
, then do whatever you need to do and then terminate yourself manually. (Thanks to Larry Gerndt for this one!)
- Using NSTimer (NSTimer->Swift)
[permalink]
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
- Populating a dictionary with predefined values (NSDictionary->General)
[permalink]
dictionaryWithObjectsAndKeys is handy if you're setting up a defaults preference dictionary (or text attributes, or any dictionary) by hand:
defaults = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 25.0], @"density",
[NSNumber numberWithFloat: 40.0], @"rate",
nil];
- Changing a file to -kb after it's been added (CVS->General)
[permalink]
% cvs admin -kb filename
- Checking .nib and .pbproj files into CVS (CVS->General)
[permalink]
You'll need to use the cvswrappers
file provided in the devtools. The easiest thing to do is to copy it to your homedir and prepend a dot:
% cp /Developer/Tools/cvswrappers ~/.cvswrappers
You can also add the contents of that fike to your repository.
.pbproj
files aren't handled by the cvswrappers by default. You'll need to add a line like this:
*.pbproj -k 'b' -f '/Developer/Tools/cvs-unwrap %s' -t '/Developer/Tools/cvs-wrap %s %s' -m 'COPY'
- Dealing with the past (CVS->General)
[permalink]
To check out the world as it was 3 days ago:
% cvs checkout -D "3 days ago" blork
Or via a date (month/day/year)
% cvs checkout -D "10/17/2002" fnord
or recently in time
% cvs diff -D "1 hour ago" ook.blah
- Getting cvs update to build new directories (CVS->General)
[permalink]
% cvs update -d [whatever]
- Merging a Branch with new directories in CVS (CVS->General)
[permalink]
When merging a branch back into the trunk (using the update command) be sure to use the -d
option. Otherwise CVS will just ignore any directories added in the branch.
% cvs update -Ad #switch to the trunk (if you haven't already)
% cvs update -d -j branch_name
[resolve conflicts]
% cvs commit -m "whatever"
(Courtesy of Jeremy Stein)
- Recursively adding directories (CVS->General)
[permalink]
To recursively add stuff in CVS:
% find . -type d -print | grep -v CVS | xargs cvs add
% find . -type f -print | grep -v CVS | xargs cvs add
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 -kb
for binary files.
- Showing the log comments between a set of revisions (CVS->General)
[permalink]
% cvs log -r1.2:1.5 multiprocessing.txt
- Basic NSURLConnection delegate methods (NSURL->General)
[permalink]
- (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;
- Loading a string from a website (NSURL->General)
[permalink]
(this will block until it loads)
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];
- Loading an image from a website (NSURL->General)
[permalink]
(this will block until it loads) NSURL *url;
NSData *data;
NSImage *blork;
url = [NSURL URLWithString: @"http://borkware.com/hacks/random-pic"];
data = [url resourceDataUsingCache: NO];
blork = [[NSImage alloc] initWithData: data];
- Open an URL in iphone safari (NSURL->General)
[permalink]
NSURL *url = [NSURL URLWithString: @"http://cuteoverload.com/2006/09/12/xtreme_gluttony/"];
[[UIApplication sharedApplication] openURL: url];
- CGShading (Graphics->General)
[permalink]
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.
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);
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):
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
- Clipping to a CGPath (Graphics->General)
[permalink]
CGPathRef border = ... get a path from somewhere;
CGContextRef context = UIGraphicsGetCurrentContext ();
CGContextSaveGState (context); {
CGContextAddPath (context, border);
CGContextClip (context);
// draw draw draw
} CGContextRestoreGState (context);
- Converting HSL to RGB (Graphics->General)
[permalink]
// 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
- Converting RGB to HSL (Graphics->General)
[permalink]
Some color manipulations are easier in HSL than RGB.
// 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
- Converting degrees to radians and back (Graphics->General)
[permalink]
Most programming interfaces that deal with angles talk in radians (2 pi radians in a circle), like CGAffineTransformRotate
. I personally think better in degrees (360 degrees in a circle).
Convert degrees to radians by multiplying by 180 / pi
.
Convert radians to degrees by multiplying by pi / 180
I have a couple of #defines I stick into a common header for projects that need it;
#define BWDegToRad(d) ((d) * M_PI / 180.0)
#define BWRadToDeg(r) ((r) * 180 / M_PI)
- Creating a CGImageMask from a UIImage (Graphics->General)
[permalink]
- (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
- Draw a string centered in a rectangle (Graphics->General)
[permalink]
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];
- Drawing a gradient (Graphics->General)
[permalink]
- (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
- Drawing a string centered in a rectangle (Graphics->General)
[permalink]
- (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
- Drawing outlined / filled text (Graphics->General)
[permalink]
I wanted to draw text like this:
- (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
(Thanks to Chris Liscio for handy pointers)
- Getting a CGContext from Cocoaland (Graphics->General)
[permalink]
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:
#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
- Grabbing the bits from the screen (Graphics->General)
[permalink]
(Many thanks to Paul Sobolik for giving us this an updated and more reliable version)
- (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
- Pouring color through a mask (Graphics->General)
[permalink]
Given a CG Image mask, fill it with color.
- (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
- Push/pop CGContext state (Graphics->General)
[permalink]
CGContextRef context = ...;
...
CGContextSaveGState (context); {
[drawable drawWithContext: context
inRect: bounds
withMetrics: self.metrics]; // or whatever drawing you're doing
} CGContextRestoreGState (context);
Edit: Gus Meuller of Flying Meat fame has a block-based utility that does this:
void FMCGContextHoldGState (CGContextRef context, void (^block)()) {
CGContextSaveGState(context); {
block ();
} CGContextRestoreGState(context);
}
and if you're wanting to do something similar with NSGraphicsContext
s:
void FMNSContextHoldGState (void (^block)()) {
[NSGraphicsContext saveGraphicsState]; {
block ();
} [NSGraphicsContext restoreGraphicsState];
}
- Rounded rect path (Graphics->General)
[permalink]
Returned path is refcounted, so you'll need to CFRelease
when done.
- (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
If you're not in CGLand (or not needing the same code to work on the desktop and device), there's also UIBezierPath's -bezierPathWithRoundedRect:cornerRadius:
(for all corners), and -bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:
(for some arbitrary subset of corners), and NSBezierPath's -bezierPathWithRoundedRect:xRadius:yRadius:
. Muchos Thankos to Paul Collins for the reminder.
- Using CGLayers (Graphics->General)
[permalink]
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:
- (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
And then to draw the layer at a particular point (like replicating it a bunch of different points as done here):
for (i = 0; i < pointCount; i++) {
CGContextDrawLayerAtPoint (context, locations[i], layer);
}
- Using a stretchable UIImage (Graphics->General)
[permalink]
Make an image somewhere (like in your -init
):
_timeIndicatorImage = [[[UIImage imageNamed: @"ride-profile-time-indicator"]
stretchableImageWithLeftCapWidth: 0.0
topCapHeight: 1.0] retain];
Then draw it. In this case it's a vertical indicator.
- (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
- What are the iDevice Icons? (Graphics->General)
[permalink]
Apple's Technical Q&A 1686 has the lowdown on the icons you need. As of July 2011, this is the set:
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
Then in your info.plist, make an "Icon files" entry (an array), and list all the Icon*.png files.
- Breaking on <Error>: doClip: empty path. (Graphics->Debugging)
[permalink]
Sometimes you get the message <Error>: doClip: empty path.
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 CGPostError
.
- Turning a string into a path (Graphics->NSString)
[permalink]
- (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
- Disabling "Return moves editing to next cell" in TableView (NSTableView->General)
[permalink]
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.
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.
// 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
(Thanks to Steven Jacowski for a tweak that ends editing on clicks on different cells)
- Dragging feedback only between rows (NSTableView->General)
[permalink]
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 NSTableViewDropAbove
- (NSDragOperation) tableView: (NSTableView*) tableView
validateDrop: (id ) info
proposedRow: (int) row
proposedDropOperation: (NSTableViewDropOperation) op
{
int result = NSDragOperationNone;
if (op == NSTableViewDropAbove) {
result = NSDragOperationMove;
}
return (result);
} // validateDrop
- Handling double-clicks (NSTableView->General)
[permalink]
Use NSTableView
's -setDoubleAction:
method, and supply it a standard IBAction-style method selector.
You may need to also do -setTarget:
. double-clicks get sent if the column isn't editable, so you may need to grab the column and do a -setEditable: NO
on it.
- Making a table view a drag source (NSTableView->General)
[permalink]
If you want to make your NSTableView a source for a drag and drop operation, you need to implement
- (BOOL) tableView: (NSTableView *) tableView
writeRowsWithIndexes: (NSIndexSet *) rowIndexes
toPasteboard: (NSPasteboard *) pboard;
in your table view data source (and don't forget to hook up the datasource if you use bindings to populate the tableview)
(Thanks to Rob Rix for updating this with more modern API)
- Preventing the return key from editing the next cell (NSTableView->General)
[permalink]
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:- (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
- Putting the ordering triangles in table columns (NSTableView->General)
[permalink]
Put this somewhere (maybe in your mouseDownInHeaderOfTableColumn
or didClickTableColumn
:
NSImage *indicatorImage;
if (sortAscending) {
sort your data ascending
indicatorImage = [NSImage imageNamed: @"NSAscendingSortIndicator"];
} else {
sort your data descending
indicatorImage = [NSImage imageNamed: @"NSDescendingSortIndicator"];
}
sortAscending = !sortAscending;
[tableView setIndicatorImage: indicatorImage
inTableColumn: tableColumn];
[tableView reloadData];
- Responding to selection changes (NSTableView->General)
[permalink]
- (void) tableViewSelectionDidChange: (NSNotification *) notification
{
int row;
row = [tableView selectedRow];
if (row == -1) {
do stuff for the no-rows-selected case
} else {
do stuff for the selected row
}
} // tableViewSelectionDidChange
If you have more than one tableview, the notification's object is the tableview that had the selection change.
- Table View drag destination on or between rows (NSTableView->General)
[permalink]
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 -setDropRow:dropOperation:
, like so:
- (NSDragOperation) tableView: (NSTableView *) view
validateDrop: (id ) 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
and in the acceptDrop
method, look at the operation:
- (BOOL) tableView: (NSTableView *) view
acceptDrop: (id ) 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
- Tableview DataSource methods (NSTableView->General)
[permalink]
Your datasource needs to implement these two bad boys:
- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
- (id) tableView: (NSTableView *) tableView
objectValueForTableColumn: (NSTableColumn *) tableColumn
row: (NSInteger) row
- Add pgplsql support (Postgresql->General)
[permalink]
% createlang plpgsql template1
- Adding a column to a table (Postgresql->General)
[permalink]
alter table pfo_survey_response_2006 add column section text
- Backing up a database (Postgresql->General)
[permalink]
% pg_dump databasename > db.out
- Connecting to a user / database (Postgresql->General)
[permalink]
Say you have your primary database user as 'nsadmin', and they have a database called 'openacs'. If you're using the default pg_hba.conf
, you can connect to the database, logged in as an ordinary person, by doing
% psql -U nsadmin opeancs
- Creating a new database (Postgresql->General)
[permalink]
% createdb database-name
- Creating a new user (Postgresql->General)
[permalink]
psql=# create user bork with password 'bork';
You can also do this for a passwordless user:
% crateuser bork
- Inserting a time column (Postgresql->General)
[permalink]
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:
psql=# select to_timestamp('10:45am', 'HH12:MIam')::timetz::time;
to_timestamp
--------------
10:45:00
(1 row)
- Limiting selections to X number of rows (Postgresql->General)
[permalink]
use the "limit
" statement:
select stuff from some_table
where stuff.blah > 12
limit 5
- Logging in as a specific user (Postgresql->General)
[permalink]
% psql database-name user-name
- Logging into a remote database (Postgresql->General)
[permalink]
On machine tower, dbname borkdb, user nettest
% psql -h tower borkdb nettest
- Manually starting the database (Postgresql->General)
[permalink]
% pg_ctl -D /usr/local/pgsql/data -l /tmp/pgsql.log start
- Next value from a sequence (Postgresql->General)
[permalink]
insert into blah (id, stuff)
values (nextval('sequence_name'), 'stuff');
- Restoring a Database (Postgresql->General)
[permalink]
% psql -d databasename -f db.out
- Seeing available databases (Postgresql->General)
[permalink]
% psql -l
(lower case Ell)
- Selecting a random row from a table (Postgresql->General)
[permalink]
use the random()
function.
select stuff.src, stuff.width, stuff.height from
(select src, width, height,
random()
from bw_eyes
order by 4) stuff
limit 1
This selects a random row from bw_eyes
and returns the interesting data from it. This query is used on the quickies pages to randomly select an image of eyes staring back.
- Time interval math to integer (Postgresql->General)
[permalink]
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:
select current_timestamp()::date - entry_date::date as days_old from kb_nuggets;
- Turning off the psql pager (Postgresql->General)
[permalink]
wplug-oacs=# \pset pager
Pager usage is off.
- Using tree_sortkey (Postgresql->General)
[permalink]
Jade Rubick has some brief documentation on using the postgresql tree_sortkey stuff.
- pg interval math (Postgresql->General)
[permalink]
Older versions of pg would support select blah from whatever where date-column > (current_timestamp - 21)
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:
select to_char (whenx, 'fmDD fmMonth YYYY') as pretty_when, text
from blog
where whenx > (current_timestamp - interval '21 days')
order by whenx desc
- Increasing memory buffer sizes (Postgresql->Administration)
[permalink]
If you try upping your shared_buffers
in your postgresql.conf
, and get an error similar to this:
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.
You should increase your the shmmax
parameters using tips in the Unix administration quickies.
- Darwin Website (Darwin->General)
[permalink]
Since I can never remember this, it's at developer.apple.com/darwin.
- Darwin's CVSROOT string (Darwin->CVS)
[permalink]
for read-only checkouts:
setenv CVSROOT :pserver:username@anoncvs.opensource.apple.com:/cvs/Darwin
- Appending to the end of a textview (NSTextView->General)
[permalink]
NSRange range;
range = NSMakeRange ([[textView string] length], 0);
[textView replaceCharactersInRange: range withString: string];
And Peter Hosey provided a one-liner that may suit your needs better:
[[[textView textStorage] mutableString] appendString: string];
- Deleting the selected text (NSTextView->General)
[permalink]
[textView delete: nil]
- Determining the paragraph style at a particular point (NSTextView->General)
[permalink]
- (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
- Finding the span of a paragraph (NSTextView->General)
[permalink]
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:
NSRange selectedRange = [textview selectedRange];
NSTextStorage *storage = [textview textStorage];
effectiveRange = [[storage string] paragraphRangeForRange: selectedRange];
- Forcing validation of pending text field entries (NSTextView->General)
[permalink]
[window makeFirstResponder: window]
- Moving insertion point to the end (NSTextView->General)
[permalink]
NSRange range = { [[textView string] length], 0 };
[textView setSelectedRange: range];
- Moving insertion point to the start (NSTextView->General)
[permalink]
NSRange zeroRange = { 0, 0 };
[textView setSelectedRange: zeroRange];
- Replacing NSTextView's contents with an attributed string (NSTextView->General)
[permalink]
[[textView textStorage] setAttributedString: theNewString];
- Restricting typed in text to just digits (NSTextView->General)
[permalink]
Create a subclass of NSNumberFormatter
, add this method, and hook up the formatter to your text fields.
- (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
- Scrolling to the end of a textview (NSTextView->General)
[permalink]
NSRange range;
range = NSMakeRange ([[textView string] length], 0);
[textView scrollRangeToVisible: range];
I've heard that scrollRangeToVisible
is O(N) for the length of the text. So be on the lookout if you have lots of text involved.
- Show NSTextField text in gray if the field is disabled (NSTextView->General)
[permalink]
NSTextField doesn't change its text color if the field is enabled or disabled (?) Even more bizarre, using NSColor's disabledControlTextColor
won't draw the text in the disabled color. You need to use the secondarySelectedControlColor
, which supposedly is for active controls that don't have focus. Go figure.
To do it yourself, subclass NSTextField and override setEnabled:
to change the color:
- (void) setEnabled: (BOOL) flag
{
[super setEnabled: flag];
if (flag == NO) {
[self setTextColor: [NSColor secondarySelectedControlColor]];
} else {
[self setTextColor: [NSColor controlTextColor]];
}
} // setEnabled
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 done the legwork to figure out the underlying problem.)
- Truncating text in the middle of a string (NSTextView->General)
[permalink]
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:
[[textfield cell] setLineBreakMode: NSLineBreakByTruncatingMiddle]
(Thanks to Daniel Jalkut for this one)
- scrolling to the beginning of a textview (NSTextView->General)
[permalink]
NSRange zeroRange = { 0, 0 };
[textView scrollRangeToVisible: zeroRange];
- Getting the contents of a TextView as an NSString (NSTextView->NSString)
[permalink]
NSString *commitMessage;
commitMessage = [[commitTextView textStorage] string];
- Changing checkbox toggle state (NSControl->General)
[permalink]
[myCheckbox setState: NSOffState]
NSOnState
also works.
- Respond to every keystroke in a textfield (NSControl->General)
[permalink]
Make your object a delegate of the textfield, and add this NSControl delegate method:
- (void) controlTextDidChange: (NSNotification *) notification
{
// do work here, like count the characters or something
} // controlTextDidBeginEditing
- 'join' an array of strings into a single string (NSString->General)
[permalink]
NSArray *chunks = ... get an array, say by splitting it;
string = [chunks componentsJoinedByString: @" :-) "];
would produce something like
oop :-) ack :-) bork :-) greeble :-) ponies
- 'split' a string into an array (NSString->General)
[permalink]
NSString *string = @"oop:ack:bork:greeble:ponies";
NSArray *chunks = [string componentsSeparatedByString: @":"];
- Converting string to an integer (NSString->General)
[permalink]
NSString *string = ...;
Similarly, there is a floatValue
and doubleValue
NSString
methods.
-(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; }(Muchos thankos to Jared Crawford for a more modern implementation)
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); }];and if you're in 10.5 or earlier:
- (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
Localizable.strings
that lives in your English.lproj
directory (or whatever localization directory is appropriate). It has this syntax:
"BorkDown" = "BorkDown"; "Start Timer" = "Start Timer"; "Stop Timer" = "Stop Timer";That is, a key followed by a localized value.
In your code, you can then use NSLocalizedString()
or one of its variants:
[statusItem setTitle: NSLocalizedString(@"BorkDown", nil)];The second argument is ignored by the function. Obstensively it is a
/* comment */
in the strings file so that you can match the key back to what it is supposed to actually be.
NSlog
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 %@
, which the printf()
family won't do. Here's a some code that'll do that.#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
- (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); } // prettyNameThis 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.
NSString *filebase = [baseName stringByReplacingOccurrencesOfString: @" " withString: @""];
NSMutableString *mstring = [NSMutableString stringWithString:string]; NSRange wholeShebang = NSMakeRange(0, [mstring length]); [mstring replaceOccurrencesOfString: @" " withString: @"" options: 0 range: wholeShebang]; return [NSString stringWithString: mstring];(this can also be used for generic string manipulations, not just stripping out newlines).
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.
NSRange range = [[string name] rangeOfString: otherString options: NSCaseInsensitiveSearch];
[[NSDate date] descriptionWithCalendarFormat: @"%B %e, %Y" timeZone: nil locale: nil](Thanks to Mike Morton for this one)
NSString *ook = @"\n \t\t hello there \t\n \n\n"; NSString *trimmed = [ook stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; NSLog(@"trimmed: '%@'", trimmed);produces
2009-12-24 18:24:42.431 trim[6799:903] trimmed: 'hello there'
- (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
@implementation NSString (PasteboardGoodies) - (void) sendToPasteboard { [[NSPasteboard generalPasteboard] declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner:nil]; [[NSPasteboard generalPasteboard] setString: self forType: NSStringPboardType]; } // sendToPasteboard @end // PasteboardGoodiesThanks to Dan Jalkut for this tip.
- (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
/usr/share/dict/words
or from a script, and you want to process them. In this case turning a bunch of strings into floats:
NSMutableArray *altitudes = [NSMutableArray array]; NSString *altitudeString = [self altitudeStringFromGoogle: coords]; [altitudeString enumerateLinesUsingBlock: ^(NSString *line, BOOL *stop) { float value = [line floatValue]; [altitudes addObject: [NSNumber numberWithFloat: value]]; }];
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 }
-windowForSheet
(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).
For folks stuck in 10.2 land, or who want to do it themselves, there's always this:
- (NSWindow *) window { NSArray *windowControllers; windowControllers = [self windowControllers]; NSWindowController *controller; controller = [windowControllers objectAtIndex: 0]; NSWindow *window; window = [controller window]; return (window); } // window
defaults write com.flyingmeat.acorn NSRecentDocumentsLimit 137(thanks to Gus Mueller for this one)
keepBackupFile
:
- (BOOL) keepBackupFile { return (YES); } // keepBackupFileThis will do the tilde thing to the older version of the file.
NSDocumentController *documentController; documentController = [NSDocumentController sharedDocumentController]; BWBorkStitchDocument *noteDocument; noteDocument = [documentController documentForWindow: noteWindow];
[window frame]
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 NSWindowController
's cascading, otherwise it'll honor your width and height, but not your origin. In your document's windowControllerDidLoadNib
method, you'll need:[[self windowController] setShouldCascadeWindows: NO]; NSWindow *window; window = [self window]; [window setFrame: loadedWindowBounds display: YES];
- (NSString *) windowTitleForDocumentDisplayName: (NSString *) displayName { NSString *string; string = [NSString stringWithFormat: @"Overview of %@", displayName]; return (string); } // windowTitleForDocumentDisplayName
- (void) windowDidLoad { [super windowDidLoad]; [self setShouldCascadeWindows: NO]; [self setWindowFrameAutosaveName: @"pannerWindow"]; // and any other windowDidLoad work to be done } // windowDidLoad
validateMenuItem:
and do something like this:
- (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
NSMenuWillSendActionNotification
is posted to the notification center before the action is invoked.
gettimeofday()
to get sub-second granularity for timing:#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);
time_t now; now = time(NULL); struct tm *tm; tm = localtime (&now); char timestamp[200]; strftime (timestamp, 200, "%m/%d/%Y-%H:%M", tm);
- (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
NSFileManager
'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. appropriateForURL
is is advanced - mainly of use if you're going to be doing an atomic swap of files.
In Swift 4:
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 }ObjC:
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); }(looking how to do it with paths? )
NSURL *mediaDirectory = ...; NSArray *contents = [fm contentsOfDirectoryAtURL: mediaDirectory includingPropertiesForKeys: @[] options: 0 error: &error]; if (contents == nil) { NSLog (@"could not contents of %@ - %@", mediaDirectory, error); return nil; }There's also
enumeratorAtURL:includingPropertiesForKeys:...
that gives you back a directory enumerator.
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];If you want to stick the tidle before the file extension (so the finder could open
ook~.tiff
), try this:
NSString *pathExtension = [filename pathExtension]; if (!pathExtension) { tildeFilename = [filename stringByAppendingString: @"~"]; } else { tildeFilename = [NSString stringWithFormat: @"%@~.%@", [filename stringByDeletingPathExtension], pathExtension]; }(Thanks to Peter Hosey for this one)
NSFileManager *defaultManager; defaultManager = [NSFileManager defaultManager]; [defaultManager removeFileAtPath: tildeFilename handler: nil];The handler is an object that will be sent message, like
fileManager:shouldProceedAfterError:
if something goes wrong during the removal.
NSString *filename = @"/this/is/my/file/name"; NSData *data = // get NSData from somewhere, like NSPropertyListSerialization [data writeToFile: filename atomically: NO];
% setenv XMLLINT_INDENT " " % xmllint --format oopack.xml > blah.xml
NSData *data = // get NSData from somewhere, like NSFileManager if (data) { myRootObject = [NSPropertyListSerialization propertyListFromData: data mutabilityOption: NSPropertyListMutableContainers format: nil errorDescription: nil]; }For the mutability options of the resulting object, you can also use
NSPropertyListImmutable
and NSPropertyListMutableContainersAndLeaves
NSDictionary
, NSArray
,NSNumber
, NSString
, NSData
) as XML like this:
NSData *data; data = [NSPropertyListSerialization dataFromPropertyList: notes format: NSPropertyListXMLFormat_v1_0 errorDescription: nil];Then write out the data using
NSFileManager
, or whatever other mechanism you wish.
- (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
StitchEdit[3522] *** class error for 'BWRawPath': class not loaded
In a convenient place (like in the +load method of your class), use NSUnarchiver's decodeClassName:asClassName:
@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. } // loadNote that this won't help you if you're wanting to rename something that was added via
encodeValueOfObjCType:
. You'll have to write some code to unarchive your data using the old @encode(oldName)
and then re-archive it as @encode(newName)
(Thanks to Greg Miller for spotting an error in this quickie)
- (NSData *) dataRepresentationOfType: (NSString *) aType { NSData *data; data = [NSArchiver archivedDataWithRootObject: group]; return (data); } // dataRepresentationOfType
- (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
NSCoding
protocol.
- (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
- (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
- (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
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);Of course, you can use different
NSFileHandle
methods for different styles of reading, and you can make a pipe for standard input so you can feed data to the task.
NSTasks
and a bunch of NSPipes
and hook them together, or you can use the "sh -c
" 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.
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.One important note:, don't feed in arbitrary user data using this trick, since they could sneak in extra commands you probably don't want to execute.
stripper.pl
which removes everything that looks like an HTML / SGML / XML tag:
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.#!/usr/bin/perl while (<>) { $_ =~ s/<[^>]*>//gs; print $_; }
This method will take a string and feed it through the perl script:
- (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
First step was to make the tools inherit from NSResponder:
@interface BWTool : NSResponder { // blah } // ... more blah @end // BWToolThen, 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:
- (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; } // setDrawToolAnd now tools can have action methods, and menu items that enable and disable appropriately.
- (NSString*) title { return (title); } // title - (void) setTitle: (NSString *) newTitle { [title autorelease]; title = [newTitle copy]; } // setTitleFor maximum safety in the face of threads, use this:
- (NSString *) title { return [[title retain] autorelease]; } // titleThis puts
title
into the current thread's autorelease pool, so title
is protected from being destroyed by someone else in another thread.
- (void) setTitle: (NSString *) newTitle { // use a mutex or NSLock to protect this if (title != newtitle) { [title release]; title = [newTitle copy]; } } // setTitleBy making a copy of the object, you're protected from another thread changing the value underneath you.
id
instance variable in your class, and make methods to set and get it:id delegate; ... -(void) setDelegate: (id) del { delegate = del; // you don't need to retain it } // setDelegate - (id) delegate { return (delegate); } // delegate
NSObject
so the compiler won't generate warnings, and also to document the delegate methods you support:@interface NSObject(BWStitchViewDelegate) - (BOOL) shouldHandleStuff: (NSData *) data; @end
... if ([delegate respondsToSelector: @selector(shouldHandleStuff:)]) { BOOL blah; blah = [delegate shouldHandleStuff: somedata]; if (blah) ... }
- (BOOL) respondsToSelector: (SEL) aSelector { NSLog (@"%s", (char *) aSelector); return ([super respondsToSelector: aSelector]); } // respondsToSelectorThis takes advantage of an implementation detail where the
SEL
is actually a pointer to a C string
[themStitches sortUsingSelector: @selector(compareByRowLocation:)];And this selector on the BWCrossStitch class:
- (NSComparisonResult) compareByRowLocation: (BWCrossStitch *) thing2;Figure out which is lesser or greater, and return one of these. Note that
self
is the object's logical value, not the actual value of the self
pointer.
NSOrderedAsending
if self
< thing2
NSOrderedDescending
if self
> thing2
NSOrderedSame
if self
== thing2
NSColor
you used was created in Device space (e.g. colorWithDeviceRed:green:blue:alpha:
) If you use a Calibrated color (colorWithCalibratedRed:green:blue:alpha:
), the triangle goes away.
extern NSString *BWStitchGroup_VisualAttributeChangeNotification;
In the .m
file:
NSString *BWStitchGroup_VisualAttributeChangeNotification
= @"BWStitchGroup Visual Attribute Change Notification";
...
NSNotificationCenter *center; center = [NSNotificationCenter defaultCenter]; [center postNotificationName: BWStitchGroup_VisualAttributeChangeNotification object: self];If you want to pass a userinfo dictionary, you can use a variant method:
NSDictionary *userInfo = ...; [center postNotificationName: BWStitchGroup_VisualAttributeChangeNotification object: self userInfo: userInfo];
NSNotificationCenter *center; center = [NSNotificationCenter defaultCenter]; [center addObserver: self selector: @selector(groupVisualChange:) name: BWStitchGroup_VisualAttributeChangeNotification object: group];Where the selector looks like this:
- (void) groupVisualChange: (NSNotification *) notification { // do stuff with the notification } // groupVisualChange
-dealloc
:
NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center removeObserver: self];
#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
[center addObserverForName: kGRZoneSystem_ZoneSystemChangedNotification object: nil queue: [NSOperationQueue mainQueue] usingBlock: ^(NSNotification *notification) { [self adaptToZoneChange]; }];
File->Convert
, set these attributes:
NSSound
Addendum: NSSound is based on Core Audio and QuickTime as of 10.3, so it may be able to play .snd
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.)
xsd:date
has a format that's a subset of the full ISO8601 format. Here's a quick way to convert an xsd:date
to an NSDate. Based on a forum posting by Jens Alfke. For a full ISO 8601 parser, check out the one by Peter Hosey.
Note that thiscode
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
NSDate *today; today = [NSDate date];
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, ...)
English.lproj
.
index.html
Put in these headers:<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>Along with your actual help content.
AppName Help
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 AppName Help
directory.
<key>CFBundleHelpBookFolder</key> <string>AppName Help</string> <key>CFBundleHelpBookName</key> <string>AppName Help</string>
Set your Versioning System (VERSIONING_SYSTEM) to "apple-generic".
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.
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.
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.
To update the version number, you can use agvtool next-version or agvtool new-version.
/Library/Application Support/Apple/Developer Tools/Project Templates/
and edit the projects there.
(For Project Builder, go to /Developer/ProjectBuilder Extras/Project Templates/
and edit the projects there. The changes will take effect next time you create a new project.)
xcodebuild
is the pbxbuild
equivalent. To get a development build, you'll need to specify the style:% xcodebuild -buildstyle Development
.octest
file of interest:
% find . -name "*.octest" -print ./build/Debug/Tests.octestThen gdb
Tools/otest
:
% gdb /Developer/Tools/otestThen run it with the name of the bundle:
(gdb) run ./build/Debug/Tests.octest
ld: Undefined symbols: .objc_class_name_BWTrackerPluginYou can fix this by adding
-undefined dynamic_lookup
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.
Here's how Jens cast out that demon. It's two different edits:
To make the TOC section (left side bar) of the developer documentation default to be hidden, do as follows:
Before you begin, make the two files and their parent-folders writable.
In the file....
/Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.CoreReference.docset/Contents/Resources/Documents/documentation/Resources/CSS/frameset_styles.css
...change the line...
#bodyText { margin-left: 210px; }
...to read...
#bodyText { /* margin-left: 210px; */ margin-left:10px; /* TOC-FIX */ }
...And in the file...
/Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.CoreReference.docset/Contents/Resources/Documents/documentation/Resources/JavaScript/page.js
...add the following somewhere in initialize_page() function, for instance at the bottom, right before the closing brace...
showHideTOC('hide'); // TOC-FIX
...now you have a much better view!!
Note that you'll need to apply this patch when the docs get upgraded.
Project->New Group
. 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.
@property (strong, nonatomic) IBOutletCollection(XXLaneView) NSArray *laneViews;
~/Library/Developer/Xcode/Archives
ld: multiple definitions of symbol __ZN13CaptureWidgetD2Ev
/Users/blah/blah/build/./capture.build/Debug/capture.build/Objects-normal/ppc/capturewidget-F17F43C0.o definition of __ZN13CaptureWidgetD2Ev in section (__TEXT,__text)
/Users/blah/blah/build/./capture.build/Debug/capture.build/Objects-normal/ppc/capturewidget-F17F43C0.o definition of __ZN13CaptureWidgetD2Ev in section (__TEXT,__text)
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.
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)
perl -pi -e 's/MyDocument/BWGraphDocument/g' *.[hm] *.xcodeproj/* *.plist
mv MyDocument.h BWGraphDocument.h
mv MyDocument.m BWGraphDocument.m
mv MyDocument.nib BWGraphDocument.nib
- 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.
translate
to run the powerPC side of the world:
% xcodebuild -configuration Release -target "Test All" -project Tests.xcodeproj NATIVE_ARCH="i386 ppc" % /usr/libexec/oah/translate /Developer/Tools/otest build/Release/Tests.octest
% /Developer/Tools/otest -SenTest ClassName ./build/Debug/TheUnitTest.octest
% /Developer/Tools/otest path/to/build/Unittest/TheUnit Test.octest
compile sources
build phase. 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.
% defaults write com.apple.dt.Xcode DVTTextBeepOnNonMatchingBrace -bool NO
(In Project Builder, open the Targets tab, double-click the target, go to the "Build Settings" tab, scroll down to Build Settings, look for WARNING_CFLAGS
. Edit that, and add -Werror
to what's already there.)
% defaults write com.apple.Xcode XCShowUndoPastSaveWarning NO
% 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
blah.M
(upper case M
), or name your source file blah.mm
(two lower case M
s). It's best to use .mm
because of the case-insensitive (yet case-preserving) nature of HFS+
/Library/Application Support/Apple/Developer Tools/Project Templates/
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 main.m
, which is really annoying for those of use who treat warnings as errors. You can go into /Library/Application Support/Apple/Developer Tools/Project Templates/Application/Cocoa Application/
and fix that warning if you wish.
This
% 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
// // TapDance.h // Groovilicous // // Created by markd on 7/25/08. // Copyright 2008 __MyCompanyName__. All rights reserved. //With the __MyCompanyName__ placeholder. There is no UI to change this, for obvious reasons. (Why would anyone 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:
% defaults write com.apple.Xcode PBXCustomTemplateMacroDefinitions '{"ORGANIZATIONNAME" = "Borkware";}'Seemple, no? Zee trick, she is doone.
/usr/include
Here's an easy way to fix this:
% xcode-select --install
This will kick off a download of the tools, and install them.
This advice brought to you by a DTS incident.
You can work around it by setting this environment variable pair in your Run/Arguments scheme thing:
OS_ACTIVITY_MODE=disable
-Xanalyzer -analyzer-disable-all-checksIf you do this you should feel bad. I did this, and I feel bad.
% defaults write com.apple.dt.xcode IDEIndexingClangInvocationLogLevel 3
% defaults write com.apple.dt.Xcode IDEDisableStructureEditingCoordinator -bool YES
% defaults write com.apple.dt.Xcode DVTTextShowMatchingBrace -bool NOI <3 Zach Waldowski for this tidbit.
One way to hack around it is to tell the image to use the pixel size of the underlying image representation:
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];
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];There are also TIFF, BMP, GIF, JPEG file types in addition to PNG.
awakeFromNib
method, do [objController setContent: self];
awakeFromNib
helps prevent a retain cycle which can lead to memory leaks.
Put this in a convenient place:
+ (void) initialize { [NSTextFieldCell setDefaultPlaceholder: @"" forMarker: NSNotApplicableMarker withBinding: NSValueBinding]; } // initialize
- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object change: (NSDictionary *) change context: (void *) contextThe
NSKeyValueChangeKindKey
key in the dictionary tells you if the change was an insertion
(NSKeyValueMinusSetMutation
) or a deletion (NSKeyValueIntersectSetMutation
)
If it is an insertion, the NSKeyValueChangeIndexesKey
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.
if it a deletion, the NSKeyValueChangeIndexesKey
tells you the index where the object was deleted from, and the NSKeyValueChangeOldKey
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.
valueForUndefinedKey:
:
- (id) valueForUndefinedKey: (NSString *) key { id value; value = [self lookUpValueUsingSomeOtherMechanism: key]; if (value == nil) { value = [super valueForUndefinedKey: key]; } return (value); } // valueForUndefinedKeySome handy uses for this is using the user defaults for storing values (you can use the key directly to
[NSUserDefaults stringForKey:]
, or use it to query the contents of an NSDictionary
The counterpart for this is
- (void) setValue: (id) value forUndefinedKey: (NSString *) key
, which you can use to stash stuff into user prefs or a dictionary.
In the header file
#import <Cocoa/Cocoa.h> @interface BWSearchArrayController : NSArrayController { NSString *searchString; } - (IBAction) search: (id) sender; @end // BWSearchArrayControllerand then in the implementation:
// 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]); } // arrangeObjectsand then to set the search string:
- (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
observeValueForKeyPath:ofObject:change:context:
, (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.
Except
if you've added KVO to a subclass of NSArrayController
(and possibly all controller types), and don't call super's observeValueForKeyPath:ofObject:change:context:
, bindings won't work at all, with no warning/notice/nothing. (Courtesy of Duncan Wilcox)
[imageView bind: @"valuePath" toObject: imagesController withKeyPath: @"selection.fullPath" options: nil];In Interface Builder, "Bind To" corresponds to
imagesController
, "Controller Key" would be selection
, and "Model Key Path would be fullPath
.
Use
[imageView unbind: @"valuePath"];to remove a binding.
- (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
In Interface Builder, drag over the NSSearchField, and bind the predicate
like this:
what contains[c] $value
(assuming the attribute you're filtering is called what
)
[searchArrayController addObserver: self forKeyPath: @"selectionIndexes" options: NSKeyValueObservingOptionNew context: NULL];This makes
self
an observer of the searchArrayController
. We'll get notified when the selectionIndexes
value changes, and we'll be notified with the new value.
When the notification happens, this method (invoked against the self
used earlier) is invoked:
- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object change: (NSDictionary *) change context: (void *) context { } // observeValueForKeyPathand you can poke around the arguments to see wha'happened.
- (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
First, in the +initialize for the class that's going to be observed:
+ (void) initialize { NSArray *keys; keys = [NSArray arrayWithObjects: @"showMajorLines", @"minorWeight", @"minorColor", @"minorWeightIndex", @"minorColorIndex", @"majorWeightIndex", @"majorColorIndex", nil]; [BWGridAttributes setKeys: keys triggerChangeNotificationsForDependentKey: @"gridAttributeChange"]; } // initializeSo 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:
- (BOOL) gridAttributeChange { return (YES); } // gridAttributeChangeSo now the view can do this:
- (void) setGridAttributes: (BWGridAttributes *) a { [attributes removeObserver: self forKeyPath: @"gridAttributeChange"]; [a retain]; [attributes release]; attributes = a; [a addObserver: self forKeyPath: @"gridAttributeChange" options: NSKeyValueObservingOptionNew context: NULL]; } // setGridAttributesAnd will get updated whenever an individual attribute changes.
setValue:forKeyPath:
to stick the value back into the
bound object.
The bindings and path are ivars:
id selectionRectBinding; NSString *selectionRectKeyPath;In
bind:toObject:withKeyPath:options:
hang on to the binding
object and the key path and set up observing:
// 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];And in the mouseUp: handler, set the value back into the bound object:
// 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];
- (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]; }
selection
, using @count
.
observeValueForKeyPath:
method. Register your observer like this:
[searchArrayController addObserver: self forKeyPath: @"selectionIndexes" options: NSKeyValueObservingOptionNew context: NULL];So now self's observeValue method will get invoked when
selectionIndexes
changes in the array controller.
[[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
[[scrollView contentView] scrollToPoint: pointToScrollTo]; [scrollView reflectScrolledClipView: [scrollView contentView]];
% svn mkdir -m "initial revision" file:///usr/local/svnroot/MilleBorks
% 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
First, make the directories in the repository:
% svn mkdir -m "initial revision" file:///usr/local/svnroot/DungeonBorkventure % svn mkdir -m "initial revision" file:///usr/local/svnroot/MilleBorksThen import the different code bases:
% 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/MilleBorksThen checkout working copies of the projects:
% cd /work/and/play/area % svn checkout file:///usr/local/svnroot/MilleBorks/trunk MilleBorks % svn checkout file:///usr/local/svnroot/DungeonBorkventure/trunk DungeonBorkventure
svn status
. Add this to your ~/.subversion/config
file.
[miscellany] global-ignores = build *.mode* *.pbxuser *~.nib .DS_Store *~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.
You can search for global-ignores
in that file to see some info about the setting. You might want to check out Spring Cleaning with Subversion too.
$ zip ~/modified.zip $(svn status | grep ^M | awk '{ print $2;}')(Bash syntax)
% svnadmin create --fs-type fsfs /usr/local/svnroot/
% svn copy file:///usr/local/svnroot/HackDie/trunk file:///usr/local/svnroot/HackDie/tags/stable-1 -m "tag for HackDie internal stable release #1"
% svn status --show-updates
You can specify files and directories of interest.
% svn log -v -r 373 ------------------------------------------------------------------------ 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 ------------------------------------------------------------------------and you can look repository-wide too:
% svn log -v -r 373 file:///usr/local/svnroot/
% svn mkdir -m "initial revision" svn+ssh://borkware.com/path/to/svnroot/thingie
% svn add bunnies@2x.png svn: warning: 'bunnies' not found % svn add bunnies@2x.png@ A bunnies@2x.png(thanks to Gus Mueller, Mike Ash, and Guy English)
% svn switch --relocate http://from-address.flongswozzle.net http://to-address.borkware.comMuchos thankos to Lucas Eckels for the tip.
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):
% cd parent-directory-of-repository % svnadmin --bypass-hooks setlog ./svnroot -r 285 /tmp/oop.txtIf your repository is more than "just your own thing", you might want to follow the more complicated instructions at at the Subversion FAQ.
radius
- (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
- (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
self
has methods to get the managed object context and managed object model (Apple's CoreData Application template does)
Thanks to Evan Moseman who pointed me at the new easy way to do it:
NSManagedObjectContext *moc = [self managedObjectContext]; NSManagedObject *obj = [NSEntityDescription insertNewObjectForEntityForName :@"Condition" inManagedObjectContext: context];And the older, more manual way if you need some more control over the process:
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"];
mutableSetValueForKey:
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
NSMutableSet *employees; employees = [department mutableSetValueForKey: @"employees"]; [employees addObject: newEmployee]; [employees removeObject: sackedEmployee];
NSSortDescriptor *sorter; sorter = [[NSSortDescriptor alloc] initWithKey: @"when" ascending: YES]; [fetchRequest setSortDescriptors: [NSArray arrayWithObject: sortDescriptor]];
awakeFromInsert
:
- (void) awakeFromInsert { [super awakeFromInsert]; NSDate *date = [NSDate date]; [self setValue: date forKey: @"when"]; } // awakeFromInsert
- (void) setWhenSortDescriptors: (NSArray *) descriptors { } // setWhenSortDescriptors - (NSArray *) whenSortDescriptors { NSSortDescriptor *sorter; sorter = [[[NSSortDescriptor alloc] initWithKey: @"when" ascending: NO] autorelease]; return ([NSArray arrayWithObject: sorter]); } // whenSortDescriptorsThis 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.
Don't use the XML store, but instead use the SQLite store
If an entity has a large data blob as an attribute, you should make a separate entity for that attribute.
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.
This means that the photo data will only be loaded from the persistent store if you actually use it.
(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)
In my NSSegmentedControl category, I have a method to solve this problem:
@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
defaults write com.apple.Safari IncludeDebugMenu 1
if (window.console) { window.console.log ("ook1"); }(
window.console
exists in Safari, but not in Dashboard)
WebArchive *archive = ... get from somewhere...; WebFrame *frame = [webView mainFrame]; [frame loadArchive: webarchive];
... [someWebView setPolicyDelegate: self]; ... - (void) webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener { NSURL *url = [request URL]; if (url != nil) { [[NSWorkspace sharedWorkspace] openURL:url]; } } // decidePolicyForNavigationAction
% defaults write com.apple.Safari WebKitOmitPDFSupport -bool YESif you have the misfortune of having the Acrobat Reader plug-in, you can nuke
/Library/Internet Plug-Ins/AdobePDFViewer.plugin
So, combine the two for maximal pleasure:
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
open
it from the terminal, you get asked if you want to install it (which will move your widget to ~/Library/Widgets
, 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:
% defaults write com.apple.dashboard devmode YESYou can drag widgets out of the dashboard area and have them on-screen. Cmd-R will reload the widget.
% sdef /Applications/iTunes.app | sdp -fh --basename iTunesMakes iTunes.h
#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]; }(Thanks to of Chris "The_Tick" Forsythe)
$title$
for the page term.
tell application "VoodooPad" taunt end tell
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]];
% p4 revert -c default ...
p4 revert -awill revert all files that haven't actually be edited, leaving the edited opened.
(Thanks to TVL for this one)
p4 edit -t text [file_list] p4 submit(Thanks to TVL for this one)
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 }
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?Muchos Thankos to James Hober for this one.
Gus Mueller chimed in saying that if you use CFArrayBSearchValues
, be sure to sort with CFArraySortValues
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:
- (void) cfStringSort { CFArraySortValues((CFMutableArrayRef)self, CFRangeMake(0, [self count]), (CFComparatorFunction)CFStringCompare, NULL); }
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.
NSPredicate
and -filteredArrayUsingPredicate:
to selectively remove stuff from an array.
NSPredicate *hasZone = [NSPredicate predicateWithBlock: ^BOOL (id obj, NSDictionary *bindings) { GRProfileCueTime *cueTime = obj; return cueTime.cue.targetHeartZone != kZoneNone; }]; NSArray *justZones = [cues filteredArrayUsingPredicate: hasZone];This removes all of the elements of the array that do not have a target heart zone.
If this kind of stuff is useful for you, you might want to check out Mike Ash's collection utilities.
// We must take care here, since Intel leaves junk in high bytes of return register // for predicates that return BOOL. // For details see: // unibin chapter and verse (link currently broken courtesy of Apple) // and //comment at red-sweater. - (BOOL)filterObject:(id)obj returning:(id *)resultp { *resultp = obj; return ((BOOL (*)(id, SEL, id))objc_msgSend)(obj, operation_, object_); }The explicit cast to the expected return value gets rid of the junk.
hg status
? Make a .hgignore
at the top level of your repository. It supports glob and regex, and plain names. man hgignore
for more details.
~/.hgrc
file and make sure the permissions on the file are such that nobody else can read it.
[auth] bitbucket.prefix = bitbucket.org/markd2 bitbucket.username = markd2 bitbucket.password = B4dg3rzRuL3 bitbucket.schemes = http httpsSo now when you do a
% hg push https://bitbucket.org/markd2/borkfitno need to authorize.
~/.hgrc
[extensions] extdiff = [extdiff] cmd.chdiff = /Local/Apps/Changes.app/Contents/Resources/chdiff opts.chdiff = --waitYour Changes.app path is probably different, so be sure to change it. Or add the
chdiff
's directory to your shell path.
Now you can
% hg chdiff
And get pretty diffing.
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle: @"Back" style: UIBarButtonItemStyleBordered target: nil action: nil]; self.navigationItem.backBarButtonItem = backButton; [backButton release];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
@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
[self presentViewController: vc animated: YES completion: ^{ NSLog (@"you complete me"); }];and to get rid of it
[self dismissViewControllerAnimated: YES completion: ^(void) { NSLog (@"kompressor does not dance"); }];
[self presentModalViewController: webview animated: YES];And to get rid of it (maybe from inside of the view controller that just got modalated)
[self dismissModalViewControllerAnimated: YES];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Some Title" message: @"You look very nice today." delegate: self cancelButtonTitle: @"OK" otherButtonTitles: nil]; [alert show]; [alert release];If you want to know what got tapped, use a delegate method. The cancel button is index zero.
- (void) alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) buttonIndex { NSLog(@"foobage! %d", buttonIndex); } // clickedButtonAtIndex
-viewDidLoad
:
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle: @"Cancel" style: UIBarButtonItemStylePlain target: self action: @selector(cancel:)]; self.navigationItem.rightBarButtonItem = cancelButton; [cancelButton release];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemAdd target: self action: @selector(addNewSegment)]; self.navigationItem.rightBarButtonItem = addButton; [addButton release];
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;
self.navigationItem.hidesBackButton = YES;
- (void) navigationController: (UINavigationController *) navigationController willShowViewController: (UIViewController *) viewController animated: (BOOL) animated { if (viewController == _menuController) { [_navigationController setNavigationBarHidden: YES animated: YES]; } } // willShowViewController
- (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
GRChooseRideKindViewController *chooseKind = [[GRChooseRideKindViewController alloc] init]; [self.navigationController pushViewController: chooseKind animated: YES]; [chooseKind release];
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) } } }
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; } // unselectSelfIn 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.
- (void) longPress: (UILongPressGestureRecognizer *) recognizer { if (recognizer.state != UIGestureRecognizerStateBegan) return; Put the stuff from Adding a pop up menu thingie here self.selected = YES; // Highlight to make it doubly-obvious what's being menu'd } // longPress
UITableViewCell *cell = ...; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;The delegate method (accessoryTypeForRowWithIndexPath) has been deprecated, so don't use that.
Use UITableViewCellAccessoryCheckmark
for a checkmark.
Use UITableViewCellAccessoryNone
to remove the checkmark.
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. _tableIndex
is an array of strings for display of the index.
- (NSArray *) sectionIndexTitlesForTableView: (UITableView *) tableView { return _tableIndex; } // sectionIndexTitles - (NSInteger) tableView: (UITableView *) tableView sectionForSectionIndexTitle: (NSString *) title atIndex: (NSInteger) index { return index; } // sectionForSectionIndexTitle
if (someProperty) cell.textLabel.textColor = [UIColor grayColor]; else cell.textLabel.textColor = [UIColor blackColor];
- (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; } // canMoveRowAtIndexPathAnd then prevent rows from being dragged to the first and last position:
- (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
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row inSection: 0];
willSelectRowAtIndexPath
, return nil to reject the selection, return the passed-in indexPath to use as-is, or return your choice of selected cells.
- (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
- (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
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; if (indexPath != nil) [self doStuff];
- (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
-reloadData
, but don't. That's an awfully big hammer. Instead just reload a row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row inSection: 0]; NSArray *array = [NSArray arrayWithObject: indexPath]; [_playlistTableView reloadRowsAtIndexPaths: array withRowAnimation: UITableViewRowAnimationNone];
layoutSubviews
in your UITableViewCell subclass. Any view position changes will automatically be animated.
- (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]; } // layoutSubviewsThe
showingDeleteConfirmation
test is so you don't move things around if the user does the "swipe-right to show delete button" thing.
-canEditRowAtIndexPath:
- (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath { if (indexPath.row == 0) return NO; else return YES; } // canEditRowAtIndexPath
[self.cuesTableView setEditing: YES animated: YES];
- (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
- (void)tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath { // Do something logical with indexPath.row, etc. } // didSelectRowAtIndexPath
NSIndexPath *someRow = [NSIndexPath indexPathForRow: random() % blah.count inSection: 0]; [self.cuesTableView scrollToRowAtIndexPath: someRow atScrollPosition: UITableViewScrollPositionBottom animated: YES];You can also scroll to PositionTop and PositionMiddle. If you crash, make sure you've done a
-reloadData
on the table view prior to trying to scroll.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: index inSection: 0]; [self.tableView reloadData]; // necessary if selecting row in -viewDidLoad [self.tableView selectRowAtIndexPath: indexPath animated: YES scrollPosition: UITableViewScrollPositionNone];
cell.textLabel.backgroundColor = [UIColor redColor]; cell.contentView.backgroundColor = [UIColor redColor];
UIImage *image = [_assets iconAtIndex: indexPath.row]; if (image) cell.imageView.image = image;
// 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
Make the cell with UITableViewCellStyleSubtitle
:
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: @"UITableViewCell"] autorelease];and set the label values:
cell.textLabel.text = @"somewhere"; cell.detailTextLabel.text = @"over the rainbow";
_tableView.scrollEnabled = NO;
- (NSString *) tableView: (UITableView *) tableview titleForHeaderInSection: (NSInteger) section { NSArray *sections = [BWTermStorage sections]; return [sections objectAtIndex: section]; } // titleForHeaderInSectionYou can return a title for a footer.
You can also return a view:
- (UIView *) tableView: (UITableView *) tableView viewForHeaderInSection: (NSInteger) section;
The appropriate constants are
UITableViewCellStyleDefault
, UITableViewCellStyleValue1
, UITableViewCellStyleValue2
, and UITableViewCellStyleSubtitle
, and settable properties include imageView
, textLabel
, and detailTextLabe
l.
1) Make a nib with a single object at the top, a UITableViewCell
. Lay it out as you wish. I use view tags to get to the objects contained therein.
enum { kTermLabelTag = 1, kDetailLabelTag = 2 };
2) Register the nib for a cell reuse identifier. I do it in my -viewDidLoad
.
static NSString *g_cellReuseIdentifier = @"BWLookupCellReuseIdentifier"; // file global ... UINib *nib = [UINib nibWithNibName: @"BWLookupTableViewCell" bundle: nil]; [self.resultsView registerNib: nib forCellReuseIdentifier: g_cellReuseIdentifier];3) Dequeue the cell as usual
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: g_cellReuseIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: g_cellReuseIdentifier]; }4) Dig into the cell with the tags, and go nuts
UILabel *termLabel = (UILabel *)[cell viewWithTag: kTermLabelTag]; UILabel *detailLabel = (UILabel *)[cell viewWithTag: kDetailLabelTag]; termLabel.text = termString; detailLabel.text = definition;
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.
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 }
@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 } }
[playButton addTarget: self action: @selector(play) forControlEvents: UIControlEventTouchUpInside];
[_flobButton setImage:[UIImage imageNamed: @"greezole"] forState: UIControlStateNormal];
[_rejectButton setTitle: @"My Spoon is Too Big" forState: UIControlStateNormal];
[self.z1Button setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
You might need to include this too:
#import <QuartzCore/QuartzCore.h>
[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]; }];
hooverLayer.transform = CATransform3DRotate(CATransform3DIdentity, 90.0 * M_PI / 180.0, 0.0f, 0.0f, 1.0f);
borkFunction
, and it's called by greebleFunction
and hooverFunction
:
greebleFunction (); hooverFunction ();And say that we want to break when
borkFunction
is called by hoover, and not by greeble.
(lldb) breakpoint set -n borkFunction Breakpoint created: 1: name = 'borkFunction', locations = 1 (lldb) breakpoint command add --script-type python 1 Enter your Python command(s). Type 'DONE' to end. > thread = frame.GetThread() > caller = thread.GetFrameAtIndex(1) > if caller.GetFunctionName() != "hooverFunction": > process = thread.GetProcess() > process.Continue() > DONE (lldb) run greeble! bork! hoover! Process 9074 stopped * thread #1: tid = 0x2007, 0x0000000100000dff funcs`borkFunction + 15 at funcs.m:7, stop reason = breakpoint 1.1 ... (lldb) thread backtrace frame #0: 0x0000000100000dff funcs`borkFunction + 15 at funcs.m:7 frame #1: 0x0000000100000e5e funcs`hooverFunction + 30 at funcs.m:19 ...
(lldb) settings set target.process.env-vars OOP=ack
#import "CJSONDeserializer.h" NSData *jsonData = [NSData dataWithContentsOfFile: path]; // or from network, or whatever NSError *error; NSArray *playlists = [[CJSONDeserializer deserializer] deserializeAsArray: jsonData error: &error];
#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
[_durationPicker selectRow: row inComponent: 0 animated: NO];
- (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
// 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 #endifThen I use the GR_TARGET symbols for #if'ing in platform-specific chunklets of code.
-isEqual:
and -hash
- (BOOL) isEqual: (id) object; - (NSUInteger) hash;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(twoByThreeTap:)]; tap.numberOfTapsRequired = 2; tap.numberOfTouchesRequired = 3; [self addGestureRecognizer: tap]; [tap release];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:
// So we can get the taps. self.userInteractionEnabled = YES; self.multipleTouchEnabled = YES;
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event { UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView: self]; if (CGRectContainsPoint(self.bounds, point)) { NSLog (@"YAY!"); } } // touchesEnded
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];
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. }This doesn't deal with stuff like aspect ratios, etc.
self.groovyView.layer.cornerRadius = 5; self.groovyView.layer.borderColor = [[UIColor purpleColor] CGColor]; self.groovyView.layer.borderWidth = 1; self.groovyView.layer.masksToBounds = YES;
#import <QuartzCore/QuartzCore.h> // For layer styles ... textview.layer.borderWidth = 1.0f; textview.layer.borderColor = [UIColor blackColor].CGColor;
- (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;
@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];And here is the version for Cocoa-non-touch:
@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 // BlahViewAnd for Swift/Cocoa-touch:
func prettyColor(thanks to Step Christopher for the translation)(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 }
typedef CGRect (^GroovyBlock)(NSInteger spoon, NSString *waffle);Creates a typedef called GroovyBlock that returns a CGRect and takes an integer and a string.
@property
? This declares a property named dataUpdater
that's a block that takes two arguments.
@property (copy, nonatomic) void (^dataUpdater)(NSString *key, id newValue);
+ (void) initialize { static dispatch_once_t init_predicate; dispatch_once (&init_predicate, ^{ 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
Thanks to Dave DeLong: Its a CoreMedia error: Returned when caller passes incorrect input or output parameters
So it's a paramErr, but for CoreMedia. Something, somewhere, internally went amiss, and it's interpreting an argument as invalid.
Here are situations I've either encountered or seen on the net:
#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; } // voiceCueLengthAnd Link with the AudioToolbox framework.
textField.keyboardType = UIKeyboardTypeDecimalPad;
[self.nameField becomeFirstResponder];If you want the keyboard to animate in after your view appears, you can call this in your
-viewWillAppear:
[textField resignFirstResponder];Otherwise, you can tell the enclosing view to end editing, and it'll figure out who is editing and tell them to resign:
[self.view endEditing: YES];
- (BOOL) textFieldShouldReturn: (UITextField *) textField { // Dismiss the keyboard. [self.view endEditing:YES]; return YES; }
- (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(thanks to Frank Shearer, from a SO post)
dir (lldb.SBFrame)
#!/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)
.schema
sqlite> .schema camp 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);
codesign
tool does that.
How to use:
% cd ~/Library/Developer/Xcode/Archives/$DATE/Campwhere.xcarchive/Products/Applications % codesign -dvvv ./Campwhere.app % codesign -d --entitlements - ./Campwhere.app
% unzip Catalog.ipaStuff will appear in a directory called "Payload"
Then you can look at signing:
% codesign -dvvv Payload/Catalog.app/ 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=296as well as entitlements:
% codesign -d --entitlements - ./Payload/Catalog.app 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> ...
% git tag '0.1-demo' -m "Proof of concept demo for initial App47 distribution"Adds the tag, with the given comment.
To push the tag up to github (or wherever)
% git push --tags
git branch -D protocol-play
(the capital D if you don't care about the merged status). You might need to git push
. too.
It's gone locally, but still exists up on github. Nuke it there with:
% git push origin :protocol-play
git add
to stage a change, but now you want to see what that was. Use
% git diff --cached
git log [options] [--] [path]
-p
— show the diff (patch)
-#
— limit output (e.g. -23
to last 23 entries)
--since / --until
— commits more recent (or older) than given date
--stat
— abbreviated stats (adds/deletes, modified files)
--pretty=thing
— Make output pretty. Things include oneline, short, medium, full, fuller, (no fullest), email, raw, format:, tformat:--graph
— draw those funky git branch gtaphs
--author / --committer
— filter on specific dude / dudette
--grep
— filter on keywords in commit messages
--all-match
— Turn multiple predicates from OR into AND
Format Options
%H / %h
— hash (shortened)
%T / %t
— tree hash (shortened)
%P / %p
— parent hash (shortened)
%an / %ae / %ad / %ar
— Author name, email, date (relative)
%cd / %ce / %cd / %cr
— Committer name, email, date (relative)
%n
— newline
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 approxidate code.
% git reset --soft "HEAD~1"
git branch markd-dev git push -u origin markd-dev
% git tag -d release-cg-pt3 % git push origin :refs/tags/release-cg-pt3 % git status % git tag release-cg-pt3 7f05a3de66 % git push origin master --tagsAnd 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.
% git tag --force '0.1-demo' -m "pithy comment" And if you're using github or something % git push --tags
If your current checkout is via https (you can figure that out by doing git config -l
(ell) and looking at the remote.origin.url
). If it's https
it's not going to use your ssh keys. You'll need to change it.
If it was originally:
remote.origin.url=https://github.com/someuser/GroovyProject.git
You'd do:
% git config remote.origin.url git@github.com:someuser/GroovyProject.git
% git remote prune --dry-run originTo see what'll get removed, and then
% git remote prune originTo clean things out.
git push
tries to push all the branches, sometimes eliciting this wonderful response:
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, GitSo you can tell git to only push the currently active branch:
% git config --global push.default current
git diff
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
git diff --staged
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.
git diff HEAD
(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 git diff
and git diff HEAD
git reset --soft HEAD~1
% git checkout filename-to-revertThis checks the file out from HEAD, removing the local modificaiton. (put
--
before the file name in case you have a branch named the same as the file)
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); }
- (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
CTFramesetterRef fsetter = CTFramesetterCreateWithAttributedString (attrString); CFRange fitRange; CGSize frameSize = CTFramesetterSuggestFrameSizeWithConstraints (fsetter, CFRangeMake (0, 0), NULL, // frame attributes CGSizeMake (rect.size.width, CGFLOAT_MAX), &fitRange);
fitRange
is the range of characters that fit in. Given a height of CGFLOAT_MAX, this should be the entire string. frameSize
is the width and height the text will be wrapped in. A { 0, 0}
range means to use the whole string.
dtrace -n 'objc$target:NSUndoManager*:+alloc:entry' -c `pwd`/eClicker PresenterAnd then you get the smackdown:
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]But it's right there! The path must be getting passed around, and needs some extra backslashes:
dtrace -v -n 'objc$target:NSUndoManager*:-init*:entry' -c ./eClicker\\\ Presenter(In case the quickies stripped off the backslashes, it's three backslashes, then a space, then
Presenter
)
copyin
or copyinstr
:
invalid address (0x10c86e3ce) in action #2 at DIF offsetThis 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
:::entry
clause before the data is used.
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 :::return
clause. You'll need to hang on the pointer because the function arguments are not passed to :::return
:
syscall::open:entry { self->filename = arg0; } syscall::open:return /self->filename/ { @files[copyinstr(self->filename)] = count(); self->filename = 0; } END { trunc(@files, 5); }
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}'
Or in a more readable form:
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; }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.
# dtrace -n 'syscall::read:entry { @read[execname] = sum(arg2); }' -n 'syscall::read_nocancel:entry { @read[execname] = sum(arg2); }' dtrace: description 'syscall::read:entry ' matched 1 probe dtrace: description 'syscall::read_nocancel:entry ' matched 1 probe ^C ... mdworker 27699995 dbfseventsd 37756854 ocspd 125588322 storeagent 431376595Sure enough, it's the MacAppStore program, downloading updates even though it's been told not to.
entry
. In the return
, 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).
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; }
pointer to non-const type 'GRSpotifyLoginCompletion' (aka 'void (^)(BOOL)') with no explicit ownership
"? Check to see you don't have too many stars.
For example,
typedef void (^GRSpotifyLoginCompletion) (BOOL loggedIn); @property (strong, nonatomic) NSMutableArray *loginCompletions; ... for (GRSpotifyLoginCompletion *completion in self.loginCompletions) { completion(success); }This generates the error. The problem is the star before
completion
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.
% clang -cc1 --help 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 ...Unfortunately it doesn't show all the warning flags.
And sounds like this .
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.
(taken from Reason 101's all about the alligator. Get the dude's ebook, it's awesome.)
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) }And call it like
let time1 = BNRTimeBlock { print("groovy") // do other work. } print(" took (time1)")
print
?
Just adopt CustomStringConvertible
and implement description
:
extension Weight: CustomStringConvertible { var description: String { return "(pounds) lbs" } }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:
extension IndoorCyclingClass { override var description: String { return "(title) - (classDescription)" } }
enum FilterOptionValue { case oneSided(value: Double) case twoSided(low: Float, high: Double) }You can unpack it piecewise on demand with
if case let .twoSided(low, high) = thingieOption.value { minThingie = Int(low) upperThingie = Int(high) }
#if 0
to tear out chunk of your file.
#if
in Swift generally requires the guts of the conditional to be syntactically correct (even if it's semantically nonsense).
You can abuse the Swift version check to do something similar to #if 0
:
... #if swift(>=666) aj2n42j4n23jnjsdnfjsnfjnunrun unr unwu nudjfn jsnf jn var window: UIWindow? #endif ...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)
(Thanks to Jeremy Sherman for the idea)
NotificationCenter.default.post(name: Notification.Name("FishBattery"), object: tour)Receiving end
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() } }
(lldb) expr -l swift -- let $ook = "verb" (lldb) expr -l swift -- print("I seem to be a ($ook)") I seem to be a verbAnd if you want to call your own stuff (say the project name is
C-Interop
):
(lldb) expr -l swift -- import C_Interop (lldb) expr -l swift -- SomeClass().useAnother()This creates a new instance of
SomeClass
and calls the useAnother
method.
(Thanks to Zach Waldowski for this one.)
let detailVC = self.storyboard?.instantiateViewController(withIdentifier: "PlaylistDetailsViewController") as! PlaylistDetailsViewController detailVC.playlist = playlist self.present(detailVC, animated: true, completion: nil)
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) }
- (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
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) } }
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") } } } }
enum Tab: Hashable { case simplePlan case complicatedPlan case multiPlan var id: Self { self } } struct ContentView: View { @State private var tab = Tab.complicatedPlan var body: some View { TabView(selection: $tab) { SimplePlan(captureModel: SimpleCaptureModel.shared) .tabItem { Label("Simple Plan", systemImage: "house") } .tag(Tab.simplePlan) CompliPlan(captureModel: CompliCaptureModel.shared) .tabItem { Label("Complicated Plan", systemImage: "house.circle.fill") } .tag(Tab.complicatedPlan) MultiPlan() .tabItem { Label("Multi Plan", systemImage: "house.lodge") } .tag(Tab.multiPlan) } } }
.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
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) } }(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
.buttonStyle(CustomButtonStyle())
Button("Button Text") { print("Snorgle") }
struct PlaceholderContainerView: UIViewRepresentable { func makeUIView(context: Context) -> PlaceholderUIView { return PlaceholderUIView() } func updateUIView(_ uIView: PlaceholderUIView, context: Context) { print("update ui view (context)") } }