Archive for April, 2013

Calculating marching ants

UnknownAlthough I use a CADisplayLink to update my dashed-selection overlay, I don’t want my marching ants overlay to depend on that timer for phase.

To that end, I worked away at a solution where I specify the speed of the progression (the seconds it takes to complete a frame) and calculate an offset based on the current time. I like this timing solution a lot better than ones that update a counter every time the display link pings, plus it lets me slow down the link’s frame interval without worrying about changing the update speed.

I’ve been using this to power a user-selected area today and it’s had no problem on-device with slightly older units in testing using CA Instruments. I’m still not in love with marching ants — moving things on my screen bother me a bit — but I did like the bit about using real time you see here.

- (void) drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClearRect(context, rect);

    CGFloat dashes[] = {12, 3};
    CGFloat distance = 15;
    CGFloat secondsPerFrame = 0.75f; // adjust as desired

    NSTimeInterval ti = [NSDate timeIntervalSinceReferenceDate] / secondsPerFrame;

    BOOL goesCW = YES;
    CGFloat phase = distance * (ti - floor(ti)) * (goesCW ? -1 : 1);
    [path setLineDash:dashes count:2 phase:phase];
    [path stroke:1 color:WHITE_LEVEL(0.75, 1)];
}

Gradient helper: Adjusting color brightness

F0616

Threw this together today to help with my gradients chapter. The idea is that you work with UIColors and tweak the brightness for a contrast.

Thought it might be useful to others, so I decided to add it to a post. Let me know if it’s at all handy.


CGFloat Clamp(CGFloat a, 
    CGFloat min, CGFloat max)
{
    return fmin(fmax(min, a), max);
}

UIColor *ScaleColorBrightness(UIColor *color, CGFloat amount)
{
    CGFloat h, s, v, a;
    [color getHue:&h saturation:&s brightness:&v alpha:&a];
    CGFloat v1 = Clamp(v * amount, 0, 1);
    return [UIColor colorWithHue:h saturation:s brightness:v1 alpha:a];
}

Empty Background Pattern

f

Today, I needed a pattern to fill an “empty” area and showcase what was and was not transparent. So I created this. Hope someone finds it useful. To create a fill color from it:

[UIColor colorWithPatternImage:EmptyBGPattern()]

Annotation View: Yay or nay?

iOS Simulator Screen shot Apr 16, 2013 4.03.59 PM

 

In order to build figures for the iOS Drawing book, I’ve found myself developing a class that I call an AnnotationView. It’s a non-interactive view that you stretch over your main view controller, and you can add point markers, arrows, text labels, lines, and Bezier paths to it.

So now, I’m wondering this: is the class that I developed for creating illustrations in and of itself worth some discussion in the book? I find it really handy, especially for debugging geometric functions like point to line, segment intersection, and so forth. But would others?

I keep swaying between yes and no. It’s a pretty didactic class — I pre-determined the arrow lengths, the text fonts, etc. So there’s more paperwork in the class than any concepts. On the other hand, it’s far more useful than I first thought it would be.

What do you think?

 

Lorem Ipsum API

lorem

 

I recently stumbled across a rather nice Lorem Ipsum API. Website loripsum.net provides an on-demand lipsum generation service, where you specify the number and length of paragraphs. The API supports both plaintext and HTML output.

NSString *urlString = [NSString stringWithFormat: @”http://loripsum.net/api/%0d/short/prude/plaintext”, numberOfParagraphs];

 

Adding Visual Depth

Screen Shot 2013-04-08 at 12.12.27 PM

I played a bit with  adding depth to the color sampler app.  You can view the results at the linked video (about 30 seconds). Pretty much all the updates were created using Bezier paths, although I did end up using those to build reusable image components rather than computing the highlights for every view.

A lot of people were giving me feedback that everything on the screen felt like it was missing a dimension. A lot of the changes are quite subtle, like the slight edges around the swatches and the button highlights. The biggest change is the color adjustment wheel in the modal screen. I changed the wheel background entirely, and tried to make the center color look a bit more glassy rather than a solid.

I also added  a few improvements, which are hard to see here, into the algorithm that draws text along paths. You see this on the color adjustment wheel and on the camera sampling view.

Reversing Bezier Curves

bp1

 

bp2

 

It turns out that reversing a path is a lot trickier than it first appears. Sadly, the built in UIBezierPath option bezierPathByReversingPath is broken. Here’s what Xcode reports when that method is called on the Hello path you see above:

HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b6c4: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b784: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b794: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b7e4: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b7f4: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
HelloWorld(62165,0xacc69a28) malloc: *** error for object 0x994b814: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
Apr  8 09:04:30 Esopus-Spitzenburg.local HelloWorld[62165] : void CGPathAddLineToPoint(CGMutablePathRef, const CGAffineTransform *, CGFloat, CGFloat): no current point.

And here’s what the path created by bezierPathByReversingPath looks like:

iOS Simulator Screen shot Apr 8, 2013 9.04.34 AM

It’s not exactly the result you expect.

To create a solution, I ended up writing my own Bezier library, where I decompose each path into first, its subpaths (each subpath begins with a Move operation, and may or may not end with a Close operation), and then into the component elements.

Reversing paths turns out to be a bit trickier than I anticipated. That’s because when you reverse items, you have to take close points into account. That means a reversed path often looks like this:

  • Move to the destination point of 2nd to last element (the one that comes before the close command)
  • Add a line to the first element of the path
  • Reverse the each line and curve, using the destination for each item as the start point, and the start element as the destination
  • If you had a close command in the original path, apply that close to avoid odd line cap artifacts

Here’s the code in question. It’s still got new code smell all over it, so if you find anything I messed up please let me know. Also, it uses quite a few custom subclasses and routines, which I’ve omitted to focus on the approach rather than the details.