Where technology meets something or other
erica | 10:21 am | May 20, 2013 | Development
Question came up today about using Auto Layout but ensuring pixel boundaries. Apple writes:
The solution is to make sure that your drawing aligns on pixel boundaries rather than relying on points. You might also consider using Auto Layout for constraint-based, pixel-precise layout that works well for dynamic window changes. For more details, see Cocoa Auto Layout Guide.
and in the Auto Layout release notes:
For resolution independence, this system allows for pixel-perfect layout at non integral scale factors for interfaces designed in Interface Builder. Neither was previously possible.
erica | 4:20 pm | April 30, 2013 | Books,Development,iOS
Although 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)];
}
erica | 4:21 pm | April 24, 2013 | Books,Development
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];
}
erica | 2:30 pm | April 23, 2013 | Admin
Steve will be covering it over at TUAW. More details from Apple here.
I’m probably going to be listening on my cell phone while picking up my kid. I was hoping to help with the liveblog but the snowstorm means my husband can’t make the school run.
erica | 1:51 pm | April 19, 2013 | Books,Development
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()]
erica | 4:11 pm | April 16, 2013 | Books,Development
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?
erica | 12:35 pm | April 14, 2013 | Development
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];
erica | 6:15 pm | April 9, 2013 | Development,Fun,iOS
Here you go. Boo woo. This is sampled from TN31.
erica | 2:30 pm | April 8, 2013 | Development,iOS
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.
erica | 9:16 am | | Books,Development,iOS
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:
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:
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.