Starter Step

Author Archive

So yeah…its known that I’m a Mac guy and have sworn off Windows. However, my recent project has me building TripCase on the Windows Mobile platform. We have already built for the iPhone. Brian is hard at work on the BlackBerry version. Needless to say…we are swamped. Anyhow, I digress.

So working with C# has been actually interesting. I am finding that I “like” the language. It reminds me of Java, done better. It has features that are kinda ruby like. I have also been using Linq. Its a query language for parsing all types of enumerables. For TripCase we built RESTful webservices that all of our clients interact with. With that said, we deal alot with XML on this project. Linq has actually been a big help. I’ll try to post some examples of its use later. I’m sure you uber Linq users will laugh at my elementary understanding…but hey…for us guys getting started…examples rock!!

Recently, I needed to create a background process to offload a long export process. The easiest approach was to just utilize a rake task that would be kicked off by the system command. I ran into an issue because I needed to capture the rendering of a view to create the export file. This is an example of the code that came out of the exploration:

  task :run_export => :environment do
    av = ActionView::Base.new(Rails::Configuration.new.view_path)
    av.class_eval do
      include ApplicationHelper
    end
 
    chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
    file_name = "export_"
    10.times { file_name << chars&#91;rand(chars.size-1)&#93; }

    data = av.render(:partial => "shared/export", :locals => {:surveys => Survey.all})
    FileUtils.mkdir_p "#{RAILS_ROOT}/public/system/exports}"
    File.open("#{RAILS_ROOT}/public/system/exports/#{file_name}.xls", 'w') {|f| f.write(data) }
  end

The above rake task will create an instance of ActionView so that we can call render. My partial also needed access to some helpers I have defined in my ApplicationHelper, so it gets included. Next we generate a psuedo random file name for our export. Then we create a directory for our export files. The directory and its parents are only created if they do not exist. Lastly, we write the result of our render to our file.

Not sure if this is the best approach to this..but seems to work for me.

My original goal for Pimpifier was to just build a fun little app that others could download and say..”man look at this” to provide a few laughs for a mere .99cents. Last night I spent a few hours and integrated the Facebook connect library which now allows Pimpifier users to publish their new PimpHandle to there Facebook feed. I think it will help drive the social aspect of Pimpifier. Now we just have to wait for Apple to approve it and see what happens.

Our awesome travel app TripCase has now launched. Brian and I both work in the Travel Studios team at Sabre and we have been working on our killer travel app for the iPhone since December.  We submitted it to Apple about 4 weeks ago and it finally got approved yesterday and showed up in the AppStore early evening.  Learn more about how TripCase is the Ultimate toolkit to help you stay organized, informed, and connected when you travel by visiting http://www.tripcase.com/.

I have been working on a story: “As a user, I should be able to search for hotels around a given location so that I can add the hotel to my trip.”.

I built my basic form for the user to fill out the location information and provided my awesome continue button to get hotels:

locationform

I quickly realized that the user’s location could be resolved to more than a single place.  If the user entered in county: US and then city: Greenville, there would be 10 or so matches.  I would need the user to choose which was the correct city.  One way to do this would have been to take the user to a new UITableViewController with all the matches and ask the user to choose a match, then pop then continue on to find the hotels around the selected location.  While this would work, I dont like it.  What if it worked like Google Maps?  The Google Maps application shows the user a UIAlertView with an embedded TableView. It’s pretty clean.  It works well because the user does not need to go to a separate page to choose a location match.  I want my user to have this same experience.

If you have used the UIAlertView much, you know it is very limited on custimization opportunity.  I decided to subclass UIAlertView to build my new location selector.  I ran into a few “a ha” moments when trying to mimic the Google Apps look and feel.  First, their UIAlertView has rouned top and bottom corners that are independent of the TableView.  They are actually using a PlainStyle TableView and when you scroll the table in the alert, the table looks like it is sitting inside of a rounded rect UIScrollView.  Tricky Indeed!

Our solution was to use some transparent rounded corners with a dropshadow on the top image.  We also wanted the height of the custom alert to be dynamic based on the size of the embedded UITableView.  However, UIAlertViews do not have a frame or height property. The height of the UIAlertView is based on the length of its message property.  Our solution to this problem was to identify the number of rows in the table and add return characters to the message property of the UIAlertView.

Here is what it ended up looking like:

locationresults

In case you wanna see some code, Here ya go:

@protocol AlertTableViewDelegate

-(void)didSelectRowAtIndex:(NSInteger)row withContext:(id)context;

@end

@interface AlertTableView : UIAlertView <UITableViewDelegate, UITableViewDataSource>{
    UITableView *myTableView;
    id<AlertTableViewDelegate> caller;
    id context;
    NSArray *data;
	int tableHeight;
}
-(id)initWithCaller:(id<AlertTableViewDelegate>)_caller data:(NSArray*)_data title:(NSString*)_title andContext:(id)_context;
@property(nonatomic, retain) id<AlertTableViewDelegate> caller;
@property(nonatomic, retain) id context;
@property(nonatomic, retain) NSArray *data;
@end

@interface AlertTableView(HIDDEN)
-(void)prepare;
@end
#import "AlertTableView.h"



@implementation AlertTableView

@synthesize  caller, context, data;

-(id)initWithCaller:(id)_caller data:(NSArray*)_data title:(NSString*)_title andContext:(id)_context{
    NSMutableString *messageString = [NSMutableString stringWithString:@"\n"];
    tableHeight = 0;
    if([_data count] &lt; 6){
        for(int i = 0; i &lt; [_data count]; i++){
            [messageString appendString:@"\n\n"];
            tableHeight += 53;
        }
    }else{
        messageString = @"\n\n\n\n\n\n\n\n\n\n";
        tableHeight = 207;
    }
    
    if(self = [super initWithTitle:_title message:messageString delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:nil]){
        self.caller = _caller;
        self.context = _context;
        self.data = _data;
        [self prepare];
    }
    return self;
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [self.caller didSelectRowAtIndex:-1 withContext:self.context];
}

-(void)show{
    self.hidden = YES;
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myTimer:) userInfo:nil repeats:NO];
    [super show];
}

-(void)myTimer:(NSTimer*)_timer{
    self.hidden = NO;
    [myTableView flashScrollIndicators];
}

-(void)prepare{
    myTableView = [[UITableView alloc] initWithFrame:CGRectMake(11, 50, 261, tableHeight) style:UITableViewStylePlain];
    if([data count] &lt; 5){
        myTableView.scrollEnabled = NO;
    }
    myTableView.delegate = self;
    myTableView.dataSource = self;
    [self addSubview:myTableView];
    
    UIImageView *imgView = [[[UIImageView alloc] initWithFrame:CGRectMake(11, 50, 261, 4)] autorelease];
    imgView.image = [UIImage imageNamed:@"top.png"];
    [self addSubview:imgView];
    
    imgView = [[[UIImageView alloc] initWithFrame:CGRectMake(11, tableHeight+46, 261, 4)] autorelease];
    imgView.image = [UIImage imageNamed:@"bottom.png"];
    [self addSubview:imgView];

    
    CGAffineTransform myTransform = CGAffineTransformMakeTranslation(0.0, 10);
    [self setTransform:myTransform];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = (UITableViewCell*) [tableView dequeueReusableCellWithIdentifier:@"ABC"];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"ABC"] autorelease];
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
        cell.font = [UIFont boldSystemFontOfSize:14];
    }
    cell.text = [[data objectAtIndex:indexPath.row] description];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    [self dismissWithClickedButtonIndex:0 animated:YES];
    [self.caller didSelectRowAtIndex:indexPath.row withContext:self.context];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [data count];
}

-(void)dealloc{
    self.data = nil;
    self.caller = nil;
    self.context = nil;
    [myTableView release];
    [super dealloc];
}

@end

Yesterday, I looked at iTunes connect and saw that my contracts had been approved.  I quickly opened iTunes and searched for “PIMP” and bam there was Pimpifier in all its glory.  Go download and enjoy.  It is sure to bring you some laughs for a buck.

I have submitted the application for approval..lets see what happens.



  • Dave: I can tell you're a ruby guy because you forgot the 'return' keyword. Thanks for the tip though!
  • Chandrashekhar H M: Hi, Thanks its working fine in iOS 6 but not in iOS 7.0. Any Suggestion on this.
  • Coeur: To change a rootViewController, without all this TVNavigationController : myNewRoot = [[UIViewController alloc] init]; myNavigationController.view