Published October 21, 2014
I regularly make QuickTime screen recordings of demos for clients. While it's easy to capture the entire screen, it's tricky when you want to capture only a single window. You need to open QuickTime Player, select New Screen Recording, then drag a rectangle over the exact area of the screen where the window is located. It's that last part that's a real pain. Even worse, if you have to do multiple takes, you need to drag out the selection rectangle each time.
One day, after around ten failed takes of a demo I was trying to do with a live voiceover, I got fed up and automated the process.
You can use AppleScript to get the target window's position and to kick off the screen recording. You need to change the process name (here, it's configured to record the iOS Simulator) and possibly the window number (if the app has more than one window).
I couldn't figure out a way to get AppleScript to move or drag the mouse. Originally, this required a small C program. I was recently able to replace it with a Swift script:
The script, saved in my
~/bin directory, takes four arguments: the starting x- and y-coordinates and the x and y distance to move the mouse. For example:
~/bin/clickdrag -x 10 -y 20 -dx 200 -dy 100
This moves the mouse to pixel (10, 20) on the screen, simulates a left mouse button press, waits a half second, drags the mouse 200 pixels to the right and 100 pixels down, waits another half second, and releases the mouse button.
It's not very polished, and you may need to tweak the delays here and there to make it work, but it's better than having to do it by hand.
Published August 27, 2014
I use a lot of TextExpander snippets when doing Objective-C development. I really like Objective-C, but it can be pretty verbose at times. If I find myself typing the same thing repeatedly, I make a new snippet for it.
The snippets make extensive use of Xcode placeholder tokens. For example:
[NSString stringWithFormat:@"<#format string#>", <#args#>]
This will give you two placeholder "bubbles"; one for format string and the other for args. You can tab between the bubbles and replace them with whatever you need in that place.
;init to insert a default initializer:
if ((self = [super init]))
Notice the use of
%| which defines the cursor location after the snippet is expanded.
I also use
;iinit to insert an initializer body (
iinit for Inside Init):
if ((self = [super init]))
That's useful for writing initializers with parameters. It's indented an extra four spaces since it's going inside the method body.
Default Description Implementation
;desc to generate a
-description method that behaves like the default in
NSObject. I can then build on it from there.
- (NSString *)description
return [NSString stringWithFormat:@"<%@: %p>", NSStringFromClass([self class]), self];
UIViewController View Methods
;vwa generates a
;vwd generates a
;vda generates a
;vdd generates a
Variations for readonly, copy, strong, weak, and assign:
;pnra expands to:
@property (nonatomic, readonly, assign)
;pnrc expands to:
@property (nonatomic, readonly, copy)
;pnrs expands to:
@property (nonatomic, readonly, strong)
;pnrw expands to:
@property (nonatomic, readonly, weak)
;pna expands to:
@property (nonatomic, assign)
;pnc expands to:
@property (nonatomic, copy)
;pns expands to:
@property (nonatomic, strong)
;pnw expands to:
@property (nonatomic, weak)
The Strong-Self / Weak-Self Dance
;ws expands to:
__weak typeof(self) weakSelf = self;
;ss expands to:
__strong typeof(weakSelf) strongSelf = weakSelf;
Private Read-Write Property Declarations
Say you have a public interface for a class, like this:
@interface SomeClass : NSObject
@property (nonatomic, readonly, copy) NSString *someString;
@property (nonatomic, readonly, copy) NSDictionary *someDictionary;
If you want to generate a private declaration, simply copy the interface to the pasteboard and run
;dprivate. This is a shell script snippet, that performs the following:
pbpaste | sed -e 's/ : .*$/ ()/' -e 's/, readonly//' | grep -v '^[-+]' | grep -v '^$'
Using the example above, this would output:
@interface SomeClass ()
@property (nonatomic, copy) NSString *someString;
@property (nonatomic, copy) NSDictionary *someDictionary;
;mark expands to:
#pragma mark -
;public expands to:
#pragma mark - Public Methods
;private expands to:
#pragma mark - Private Methods
Declare and Initialize an Object using the Default Initializer
;new uses a fill-in for the type name and variable name:
%filltext:name=typename% *%filltext:name=varname% = [%filltext:name=typename% new];
If you enter
array, this will yield:
NSMutableArray *array = [NSMutableArray new];
Key-Value Observing Done Right
I've long since lost where I got these, but it's the correct way to register for KVO notifications.
Declare a context variable static to the source file with
static void *kKVOContext = &kKVOContext;
Register for KVO notifications in the initializer,
viewWillAppear, etc. with
[<#(id)#> addObserver:self forKeyPath:<#(NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:kKVOContext];
Deregister from KVO notifications in
viewWillDisappear, etc. with
[<#(id)#> removeObserver:<#(NSObject *)#> forKeyPath:<#(NSString *)#> context:kKVOContext];
And, finally, declare the KVO notification hander with
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
if (context != kKVOContext)
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
if ([keyPath isEqualToString:<#(NSString *)#>])
I used to have to look this up whenever I needed to use the documents directory. Now I use
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *<#path#> = [documentsDirectory stringByAppendingPathComponent:<#filename#>];
NSString variable is the full path to a file in the documents directory.
Common Type Names
;nsa expands to
;nsd expands to
;nss expands to
;nsma expands to
;nsmd expands to
;nsms expands to
These also work with the
;new snippet, above.
Reference the default
Make a new
NSString by calling
%key:tab% at the end highlights the format string placeholder:
[NSString stringWithFormat:@"<#format string#>", <#args#>]%key:tab%
Make a constant string whose value is equal to the variable name with
NSString *const %fill:name% = @"%fill:name%";
These are useful in the Terminal or in the Go to Folder dialog in the Finder.
;iossdks expands to:
;osxsdks expands to:
;simulator expands to:
~/Library/Application\ Support/iPhone\ Simulator
;archives expands to:
;deriveddata expands to:
Published March 24, 2014
I'm on a bit of an AppleScript kick lately. It's a frustrating language to use as it's often counterintuitive. I'm constantly looking up the right syntax to do things. But, it's a great way to patch little annoyances you encounter in Mac software.
The latest annoyance involves Tweetbot image windows. While I'm reading my timeline, I click on lots of links and images. After a while, I find I have a ton of image windows left over.
There's no good way to close all of those windows without quitting and restarting the app. Holding down ⌥ and clicking on the Window menu shows Close All bound to ⌥⌘W, but it doesn't work for me. The alternative is to click on each window and ⌘W on the keyboard, but who wants to do that?
I wrote a little AppleScript to do it. Tweetbot isn't strictly AppleScriptable, but using System Events and Accessiblity gets the job done. The script is a bit kludgy as the inner
repeat loop fails after closing three or four windows. To get around it, I wrapped it in a
try...on error block and repeat that until there's only one window left.
I've got this bound to ⇧⌘W using FastScripts.