Thursday, November 10, 2011

Dealing with expired developer and distribution certificates

So, round this time every year for the last few, I go into a small panic as I try to remember the steps to recreate my development and distribution certificates.

Fortunately, after cruising various blogs and the Developer Forums I saw a reference to this document:

http://developer.apple.com/library/ios/#technotes/tn2250/_index.html%23//apple_ref/doc/uid/DTS40009933


I worked thru the section on Deleting/Revoking Your Certificates and Starting Fresh and was done in about 10 minutes.

Waaayyy easier than anything else I've seen out there...

Wednesday, October 26, 2011

password protect a zip file on the mac

From the command line: zip -re foo.zip file1.txt file2.txt

you'll be prompted for a password

Saturday, October 1, 2011

iconv - for converting file formats

TextEdit on the mac doesn't do such a good job converting language files from the PC. I've found the following command line command works better:

iconv -f ISO-8859-1 -t UTF-16 [input file] > outputfile

Thursday, September 8, 2011

UIWebview resizing text on rotation

Ran into a multi-layered problem this morning with UIWebView. I have an app where a table view cell can pop open and reveal a UIWebView. The problem was on rotation - if I rotated the device and then popped up the cell, the text was too big. I found the fix here. I just added this to the html style tag:

-webkit-text-size-adjust: none;

Saving for future reference.

Tuesday, August 9, 2011

Clear out a corrupted Xcode 4 project state

I ran into a situation this morning where my Xcode 4 got into a bad state and wouldn't start. Beachballs galore.

Cleared it by going into my user directory, then into Library, and doing a

rm -rf "Autosave Information"

Friday, July 22, 2011

Recovering a deleted file from SVN

An Anonymous Geek did all the work on this one. I'm posting just in case his blog goes away; I suspect I'll need this again...


The command is:

> svn up -r 250 file.txt

up is short for update, the -r 250 indicates the revision that the file last existed in, and of course file.txt is the file you want to restore.

Thursday, June 23, 2011

Dynamic view reflection using CAReplicatorLayer



If you've done UIView reflection on iOS before, you've probably used a method that creates an image from the current view and redraws it with a gradient underneath the view. For many applications this is sufficient, but it has problems: it's slow, and for some objects (e.g. UIWebView) you're really not sure when the object is done drawing so you have to tweak it with arbitrary timers to create the reflection after the object is done rendering. Yuk.

An alternative is to use a CAReplicatorLayer (It was mentioned in a WWDC session this year). The CAReplicatorLayer lets you create copies of your main layer that update in real time. You specify the number of layers and then you specify their offset attributes from the primary layer.

Below is some code that uses the CAReplicatorLayer to reflect a view. The important stuff in this example is in the view's "layoutSubviews" method. Also note that to create a CAReplicatorLayer for your subview, you've got to override the subview's "layerClass" method as shown here.

Try it with a UIWebView or other scrollable, dynamically updating subview - it's pretty cool :)

UPDATE: In the interest of completeness, I've reworked some of the code to be a little more usable...



#import "Reflector.h"
#import <QuartzCore/QuartzCore.h>



@interface MySubview : UIWebView
@end

@implementation MySubview

+ (Class) layerClass
{
        return [CAReplicatorLayer class];
}

@end


@implementation Reflector

MySubview *subview;
CAGradientLayer *gl=nil;
BOOL hasReflector;

- (void)setup
{
        hasReflector = YES;
        
        [self setBackgroundColor:[UIColor blackColor]];
        subview = [[MySubview alloc] initWithFrame:self.bounds];
        [self addSubview:subview];
        [subview setBackgroundColor:[UIColor clearColor]];
        
        // . . . do whatever with the subview to create content
        [subview setScalesPageToFit:YES];
        [subview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.flickr.com"]]];

        
}

- (void)layoutSubviews
{
        
        if (hasReflector){
                int gap = 4; // gap between the subview and its reflection
                
                float h1 = ceil(self.frame.size.height *.6);         // subview height
                float h2 = self.frame.size.height - (h1 + gap);        // reflection height
                                
                // size the subview to make space for the reflection
                [subview setFrame:CGRectMake(0, 0, self.frame.size.width, h1)];
                
                // since the replicated layers will be sized using a scale
                // transform, we need to translate our absolute heights into
                // a scalar.
                double scale = (h2/h1);
                
                // configure the subviews replicator layer.  just two instances - the first is the
                // "real-life" rendering of the subview, the 2nd is the reflection
                CAReplicatorLayer *l = (CAReplicatorLayer *) subview.layer;
                l.instanceCount = 2;
                
                // position the instance transform.  the reflection instance will be
                // scaled by "scale" and is centered within the space of the original
                // instance, thus we compute "delta" by taking the original height, 
                // subtracting out the reflection layer size, and then dividing by half.
        
                
                double delta = (h1 - h2) / 2.0 ;
                CATransform3D t = CATransform3DMakeTranslation(0, (h1+gap)-delta, 0);
                t = CATransform3DRotate(t, M_PI, 1, 0, 0);
                t = CATransform3DScale(t, 1, scale, 1);
                
                l.instanceTransform = t;
                
                if (gl == nil){
                        // add a black gradient layer
                        gl = [CAGradientLayer layer];
                        CGColorRef c1 = [[UIColor colorWithRed:0 green:0 blue:0 alpha:.5] CGColor];
                        CGColorRef c2 = [[UIColor colorWithRed:0 green:0 blue:0 alpha:1] CGColor];
                        [gl setColors:[NSArray arrayWithObjects:(id)c1, (id)c2, nil]];
                        
                        [self.layer addSublayer:gl];
                }
                
                // position the gradient layer over the replication layer 2nd instance
                [gl setAnchorPoint:CGPointMake(0, 0)];
                [gl setFrame:CGRectMake(0, h1 + gap, self.frame.size.width, h2)];
                
        }
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
        self = [super initWithCoder:aDecoder];
        if (self){
                [self setup];
        }
        return self;
}
- (void)dealloc {
    [super dealloc];
}


@end

Thursday, June 16, 2011

Setting up a Mac OSX partition for beta development

In the past I've waited until final (GA) versions of MacOS, Xcode, iOS, etc, have been released so that I didn't pollute my development environment with potentially bad software. I've usually got one or two projects in development at any given time and I couldn't take the risk that I'd get into a state where I couldn't build and release the final project.

I know that some people move around their /Developer directory but that seemed a little risky to me; the possibility is high that I'd forget which state I was in at any given time. Also it doesn't help with new OS versions.

When I was at WWDC last week, someone suggested partitioning the drive and saving new stuff there. That's turned out to be a great idea. (so far, anyways)

To start with, I found a nice online reference for how to create a partition on the "for dummies" site. While the instructions in there didn't exactly match what I saw onscreen, they were close enough to get me going.

Here's what I did:

BTW, I make no warranties, express or implied, that this will work for you. Anytime you mess around with partitioning a disk you run the risk of completely screwing up your system so this is just an explanation of what worked for me.

Anyways, to create the new partition, I did the following:

  1. Went into Applications->Utilities->Disk Utility
  2. Selected the main disk on my macbook air.
  3. Selected the Partition button. At this point, a nice graphic of my disk use showed up.
  4. Selected the little "+" at the bottom left of the Partition Layout. That allowed me to then size my new partition.
  5. I made the new partition about 100 GB since I intended on using this for all beta stuff going forward.
  6. I called the new partition "Beta Software" and selected "Apply".


Next, I installed Mac OSX 10.7 (Lion) (BETA) into the new partition.

Then, to boot from the new partition, I selected the Startup Disk from Settings and selected my "Beta Software" partition. Clicked "Restart" and I was in the new environment. Anytime I want to switch back, I just change the startup disk. Sweet.

One thing I've noticed after restarting in 10.7, was that the "Beta Software" partition wasn't listed in "Finder". To do that I had to right-click on "Macintosh HD", select "Open enclosing folder", and then drag "Beta Software" into "Devices". (Maybe that'll change with the GA version of Lion).

So the good news is that I can now verify our software on early release code without screwing up my current development environment. The bad news is I've already found a major issue with our software running on iOS5. Hopefully they'll fix it so I don't have to :)

Wednesday, June 8, 2011

Merge image function

Here's a simple method in a UIImage category that I'm using to merge two images together. It's based mostly on the code here but is cleaned up and allows a few options.

UIImage+Utils.m

// NOTE! this method should only be called from the main thread because of
// UIGraphicsGetImageFromCurrentImageContext();
- (UIImage *)merge:(UIImage *)image atRect:(CGRect)pos overlay:(BOOL)overlay;
{
        UIGraphicsBeginImageContext(self.size);
        
        UIImage *bottom = (overlay)?self:image;
        UIImage *top = (overlay)?image:self;
        
        CGRect lf = CGRectMake(0, 0, self.size.width, self.size.height);
        
        CGRect bottomRect = (overlay)?lf:pos;
        CGRect topRect = (overlay)?pos:lf;
        
        [bottom drawInRect:bottomRect];
        [top drawInRect:topRect];

        UIImage *destImage = UIGraphicsGetImageFromCurrentImageContext();    
        UIGraphicsEndImageContext();
        return destImage;        
}

Thursday, June 2, 2011

duh: multiple calls to viewDidLoad

Somehow I've been writing iPhone apps for nearly three years, yet still didn't "get" (or perhaps just didn't remember) that a UIViewController's viewDidLoad method may be called multiple times. This means that I have occasionally been putting code into viewDidLoad that I assumed would only be run once per UIViewController instance.

Wrong wrong wrong.

Even beginner iOS coders know that if a memory warning occurs, hidden views are unloaded to free space for new objects - What I didn't think about (or remember) is that when the freed view needs to be used again, those views are reloaded using viewDidLoad.

Here's an example of the problem:

In the app I'm working on, I'm programmatically adding a subview in viewDidLoad. That subview's purpose is to display a picture and allow users to select from the camera roll or UIImagePickerController - To support various operations, my subview includes a data object which is initialized in viewDidLoad. In normal operations, when the parent view controller exits, it saves off the URL of the picture from the subview's data object.

However, on a device (it works great on the simulator), when the UIImagePickerController is shown, it creates a memory warning which causes my subview to be freed. When the UIImagePickerController is released, it then calls viewDidLoad again to recreate that subview, thus losing any modifications (e.g. URL) to the data object.

So that I don't forget this again, this goes into the save.txt file.

Tuesday, April 12, 2011

Using atos for reading crash logs

If I get a crash log from a user, it's no problem investigating the cause since I use XCode's "build & archive" feature to save the symbol (dSYM) files before submitting the app to iTunes. Just drag and drop the crash log into the Device Logs section of XCode and the "symbolicator" translates the memory addresses in the crash log back into code references. Easy.

However, I don't normally use Build & Archive for sending ad hoc builds to testers, so when a crash log is reported, symbolication becomes more challenging. (and impossible if the .dsym file has been deleted or overwritten) I've used symbolicatecrash previously but with various levels of success. (now that I think about it, actually just one level of success: it runs the program without error but doesn't translate the symbols)

While I was investigating what to do about this, I ran across a couple of web posts here and here that talked about using atos (address to symbol)

Unfortunately, neither post by itself worked for me, however a combination did. Basically the magic was this (most of this is in the first post referenced above):

1. Open up the crash log
2. Determine which thread crashed
3. Look for the first entry in that thread that references your application, it'll look something like the third line below:

3 UIKit 0x3230da20 0x3223a000 + 866848
4 UIKit 0x3230d97c 0x3223a000 + 866684

5 MikesCrashingApp 0x000176d8 0x1000 + 91864

4. Copy the .app, the crash log, and the .dSYM into a directory, and then open a terminal and CD into that directory
5. Run the atos command and use the address from the line in the crash log. The atos command should look something like:

atos -arch armv7 -o MikesCrashingApp.app/MikesCrashingApp 0x000176d8

If all works as expected, you should see a response like:

-[MyViewController tabBar:didSelectItem:] (in MikesCrashingApp) (MyViewController.m:79)

Which will at least tell you where the app died.

Perhaps I should just start using build and archive for adhoc builds too. :)

Sunday, April 3, 2011

Debugging EXC_BAD_ACCESS

I'm generally pretty good about tracking down EXC_BAD_ACCESS errors but I had one tonight that really got me going. Seems that sometimes I was switching UIWebView windows too fast and the delegate was being called after the UIWebView was already dealloc'd. Once I cleared the delegate before releasing, all was well.

I probably would still be looking for this problem were it not for this site which suggested the following:

Go to "Targets" tab, open "Executables" and add the following environment variables with values set to "YES" (sans quotes)

NSDebugEnabled
NSZombieEnabled
MallocStackLogging


As soon as it detects any activity with with the zombie it'll throw an exception. For more info, you can also run the gdb command: shell malloc_history <pid> <address> - in my case it was a buttload o' crap, but at least it indicated that the problem was related to a delegate.

Definitely one for the saveme.txt file.

Thursday, March 31, 2011

Using a timed NSInvocation to animate the population of a UITableView

While there's probably dozens of ways to animate the population of a UITableView, here's one way using NSInvocation with a timer. I hadn't used NSInvocation before so this was a good exercise.

The example:
1. Configures the cell
2. Sets the cell's "hidden" attribute to true
3. Creates an NSInvocation for the "setHidden" attribute of the cell
4. Configures the NSInvocation argument to set setHidden to "NO"
5. Creates a timer that fires the NSInvocation at a relative time


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        static NSString *CellIdentifier = @"Cell";
    
        UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
                
        }
        (*** set the cell data here)

        [cell setHidden:YES];
        
        NSMethodSignature * mySignature = [UITableViewCell instanceMethodSignatureForSelector:@selector(setHidden:)];
        NSInvocation * myInvocation = [NSInvocation invocationWithMethodSignature:mySignature];
        NSNumber *n = [NSNumber numberWithBool:NO];
        [myInvocation setArgument:&n atIndex:2];
        [myInvocation setTarget:cell];
        [myInvocation setSelector:@selector(setHidden:)];
        
        [NSTimer scheduledTimerWithTimeInterval:.1 * indexPath.row invocation:myInvocation repeats:NO];
        
        return cell;
}

Thursday, March 10, 2011

Using Em and Ex

For the last couple of years I've been using CSS stylesheets pretty heavily in my app designs. Typically, I'd define elements in the CSS in pixel (px) units.

However, one thing that's become an issue when designing for different size screens (e.g. ipod/ipad) is the font size. Not only should you make sure the font is appropriate, but you should also take a look at other layout content including margins and images.

There's a couple of ways of addressing this problem, but perhaps the easiest is to take advantage of the "em" and "ex" ways of defining relative sizes in CSS.

According to about 30 seconds of Google research, Em, back in the hardcopy print days, was the width of the "M" character for a given font. Ex was the height of the "X" character. So, if one wants to define a margin that scales horizontally relative to the given font, then define the margin in terms of "Em".

So using em units works great for margins. It also works great for relative fonts. And it even works for specifying image sizes.

I could write my own example, but here's a good one that's already out there

Wednesday, March 2, 2011

Converting code for Blogger

Here's a simple PERL script that converts a code file for pasting into Blogger. And yes, I used it to convert itself for this post. HOW COOL IS THAT??? LOL. If anyone knows of a free, online one that's better, lemme know.

pastable.pl



if (@ARGV < 1){
        die "Usage filename\n";
}
$filename = shift(@ARGV);

@fn = split(/\./,$filename);
$outname = $fn[0]."-pastable\.html";

open (INPUT,"<$filename");
open (OUTPUT,">$outname");

print OUTPUT "<font size=-1>";
while (<INPUT>){
        $line = $_;

        $line =~ s/\&/&amp;/g;        
        $line =~ s/</&lt;/g;        
        $line =~ s/>/&gt;/g;
        $line =~ s/\"/&quot;/g;
        $line =~ s/\n/<BR>/g;
        $line =~ s/\t/        /g;
        $line =~ s/ /&nbsp;/g;

        print OUTPUT $line;
}
print OUTPUT "</font>";

HTML5 Animated Graph / First Post

This blog is intended to be a repository of things I want to remember.

I typically use a journal for that purpose, but considering all I've gained from others sharing their experiences and code on the internets, I feel like I should share some back.

First up, here's a simple animated graph done using HTML5. I used to do a lot of graph writing in a former life but HTML5 beats the hell out of doing it in Java. I'm not sure which browsers besides Safari will render it (I know my version of Firefox didn't). Feel free to use it for whatever - there's better examples out there but this should be a decent starting place for anybody trying to use HTML5 to generate a graph. Just cut-n-paste the code below into an HTML file and it should run in Safari or other HTML5-enabled browsers.

Here's what the rendered graph should look like:



And here's the code:

<!--- Simple HTML5 Line Percentage Chart --->

<canvas width=320 height="170"></canvas>

<script>

var context = document.getElementsByTagName('canvas')[0].getContext('2d');

// fill the background (if desired)
//context.fillStyle = 'rgba(0,0,200,0.1)';
//context.fillRect(0, 0, context.canvas.width, context.canvas.height);

// sample data
var data = new Array();
for (i=0; i <= 11; i++){
        data[i] = 100 * Math.random();
}

// create some room for the margins
var xmargin=context.canvas.width * .12;
var ymargin=context.canvas.height * .15;

// create my own rect structure
function rect(x, y, w, h)
{
   this.x = x;
   this.y = y;
   this.w = w;
   this.h = h;
}

// create the "innerRect" where most of the drawing occurs
var ir = new rect(xmargin, ymargin, context.canvas.width-(1.5*xmargin), context.canvas.height - (2*ymargin));

// translate the drawing to be from the top left corner of the inner rect
context.translate(ir.x, ir.y);

// scale the Y to the Y axis (0 - 100)
context.scale(1, ir.h/100.0);

function doTitles() {
        var leftTitle = "Left Title";
        var topTitle = "The Top Title";

        context.fillStyle = 'rgba(0,0,0,.5)';
        context.strokeStyle = 'rgba(0,0,0,.5)';

        // left
        context.save();
        context.rotate((Math.PI/180.0) * -90.0);
        context.textAlign = "center";
        context.textBaseline = "middle";
        context.font = "10px arial";
        context.fillText(leftTitle, -50, -30);
        context.strokeText(leftTitle, -50, -30);
        context.restore();

        // top
        context.font = "12px arial";
        context.textAlign = "center";
        context.textBaseline = "bottom";
        context.fillText(topTitle,ir.w/2.0,0);
        context.strokeText(topTitle,ir.w/2.0,0);
}
doTitles();


// set up an inner margin so we don't crowd the left and right sides
var innermargin = 5;

// compute the x-offset between adjacent plotted points.
var offset = (ir.w-(2*innermargin)) / (data.length-1);

// initialize our data pointers
var ptr = 0;
var lastX = innermargin;
var lastY = 100-data[ptr];
ptr = 1;



function drawGrid() {
   context.save();

   var y = 100;

   // draw the outer border
   context.lineWidth = 1;
   context.strokeRect(0,0,ir.w,100);

   context.font = "10px arial";

   for (i=0; i <= 10;i++){
          context.beginPath();

        // draw Y axis tics.  For even-numbered tics, draw them longer and add a label
           context.strokeStyle = 'rgba(0,0,0,.3)';
        var x = -2;
        var x1 = 0;
        if ((i % 2) == 0){
           x = -5;
           x1 = 3;
        }
        context.moveTo(x, y);
        context.lineTo(x1, y);
           context.stroke();

        if ((i % 2) == 0){
                context.fillStyle = 'rgba(0,0,0,.5)';
                context.strokeStyle = 'rgba(0,0,0,.5)';
                context.textAlign = "right";
                context.textBaseline = "middle";
                context.fillText((i*10),0-7,y);
                context.strokeText((i*10),0-7,y);
        }

        // draw an alternating "band" effect.
        if ((i % 2) == 0 && i != 10){
             context.fillStyle = 'rgba(100,100,100,0.1)';
              context.fillRect(0, y-10, ir.w, 10);
        }
        y -= 10;
   }
        
   // now draw the x-axis tics and labels. 
   var x = innermargin;
   var y = 100;

   // if we have more than 20 tics, we're only going to show every 5th one (and also the first one)
   var domod5 = 0;
   if (data.length > 20)
        domod5 = 1;

   for (i=0; i < data.length;i++){
          context.beginPath();
        context.moveTo(x, y-2);
        context.lineTo(x, y+2);
           context.stroke();

        if (domod5 == 0 || domod5 && ((i+1)%5) == 0 || i == 0){
                context.fillStyle = 'rgba(0,0,0,.5)';
                context.strokeStyle = 'rgba(0,0,0,.5)';
                context.textAlign = "center";
                context.textBaseline = "top";
                var tx = (i+1)+"";

                context.fillText(tx,x,y+2);
                context.strokeText(tx,x,y+2);
        }
        x += offset;

    }
   context.restore();
}
drawGrid();



// draw a line segment - we'll use the setInterval to animate
function drawLine() {
   if (ptr == data.length){
        clearInterval(drawLine);
        return;
   }
   context.save();

   context.beginPath();
   context.lineWidth = 2;
   context.lineJoin = "miter";
   context.lineCap = "round";
   context.moveTo(lastX, lastY);
   lastX = lastX + offset;
   lastY = 100 - data[ptr];
   context.lineTo(lastX,lastY);
  // context.bezierCurveTo(lastX-(offset/2.0),lastY+25,lastX-(offset/2.0),lastY-25,lastX,lastY);

   context.strokeStyle = 'rgba(0,250,0,1)';
   context.shadowOffsetY = 0;
   context.shadowBlur = 12;
   context.stroke();
   context.restore();

   ptr++;
}
setInterval(drawLine, 50);

 

</script>