By Rupert
Uncategorized
What I’ve learned during the past two weeks (July 26 – Aug 8) in iOS Development?
Aug 9th
1. WWDC 2010 Sample Code. I’ve totally missed the download link from iTunes so I’m listing it here just in case. Note: You need to be registered in the iPhone Developer Program. Check out the Video Access List to know which videos are available for each developer program.
2. AdvancedTableViewCells – sample code provided by WWDC 2010 which explains several ways how to subclass a UITableViewCell.
- iPhone Development Foundation Videos: “Session 432 – Mastering iPhone Table Views”
- WWDC 2010 Session Video: “Session 128 – Mastering Table Views”
- IndividualSubviewsBasedApplicaionCell uses a sib
- CompositeSubviewBasedApplicationCell uses a custom view to drall all UI in drawRect
- HybridSubviewBasedApplicationCell = CompositeSubviewBasedApplicationCell + custom UI elements during init.
3. Play YouTube videos without exiting the application.
- Uses a webview to embed a youtube link.
- Doesn’t work on the simulator. Need to test on actual device
4. Black based Animations
- WWDC 2010 Session Videos HD: “Session 128 – Building Animation Driven Interfaces”
- It’s better to use block based animations than old style UIViewAnimations. Look at code below, after animations on the first block completes, displayContentOnPoiBoard executes..
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationCurveEaseOut animations:^{
viewPoiBoard.frame = newFrame;
viewPoiBoard.alpha = 0.8;
buttonMapNearby.alpha = 0;
buttonMapDropPin.alpha = 0;
segmentedControlMapType.alpha = 0;
}completion:^(BOOL finished){
[self displayContentOnPoiBoard:currentPoi];
}];5. Beta Testing using AdHoc Distribution
- WWDC 2010 Session Video: “Session 310 – App Publishing with iTunes Connect”
- iTunes Connect Program User Guide
Procedure:
1. From the Provisioning Portal, Add the devices
2. Create an AdHoc Distribution Provisioning Profile
3. Use Adhoc.mobileprovision in XCode under codesigning
4. Add an Entitlements.plist
5. Build
6. Try “Build and Archive”
- this is new in XCode, under “Archived Applications”, which is actually cool as you can immediately save the ipa file under “Share Application…”

7. Before I have to give out the Adhoc.mobileprovision as well, but now the ipa file is already sufficient.
6. iTunes Connect “New Version”
- Important Information about Submitting Your Apps to iTunes Connect
- Provide a version number and add metadata information e.g (“What’s New in this Version?). Once finished with all the metadata changes, click on “Ready to Upload Binary” and the App Status changes to “Waiting For Upload”.
- Upload the binary thru XCode, see next step.
7. Appstore Distribution Binary Submission
- Follow the instructions on Beta Testing (5) but instead use an Appstore Distribution Provisioning Profile instead.
- Trying “Build and Archive”, we can now “Validate Application” which validates the binary after providing your iTunes Connect username, password.
- “Submit Application to iTunes Connect” uploads the binary and changes the status to “Upload Received” then “Waiting For Review”.
8. Control when your app goes live
You Can Now Control When Your App Update Goes Live
- Pretty nifty stuff as you can now control when a version goes live, I notice there is a new “Release Control” which states “Hold for Developer Release”.
Rails Note #14: QuickStart Tutorial
Feb 7th
If you haven’t installed ruby, follow this post
Part 1: Installation and Configuration (Rails and Passenger)
1. Upgrade existing ruby gems
sudo gem list sudo gem update --system
UPDATE: Took me 4 hours figuring this out. There was a problem when i run script/console that it will say the “gem” detected was 1.0.1 although the current gem version is 1.3.5 after gem update –system. Google didn’t helped out. But I was able to nail down the problem from this post:
https://wincent.com/wiki/Upgrading_to_RubyGems_1.0.1_on_Mac_OS_X_10.5.1
In the post above, notice that rubygems 1.0.1 was installed in /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin. I guess this gem was being referenced first before the actual /usr/local/bin/gem. So I deleted this directory /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr.
Possible sources of gem installations:
/Users/rupert/.gem/ruby/1.8/gems /Library/Ruby/Gems/1.8/gems
rupert:1.8 rupert$ gem env RubyGems Environment: - RUBYGEMS VERSION: 1.3.5 - RUBY VERSION: 1.8.6 (2008-08-08 patchlevel 286) [i686-darwin9.5.0] - INSTALLATION DIRECTORY: /usr/local/lib/ruby/gems/1.8 - RUBY EXECUTABLE: /usr/local/bin/ruby - EXECUTABLE DIRECTORY: /usr/local/bin - RUBYGEMS PLATFORMS: - ruby - x86-darwin-9 - GEM PATHS: - /usr/local/lib/ruby/gems/1.8 - /Users/rupert/.gem/ruby/1.8 - GEM CONFIGURATION: - :update_sources => true - :verbose => true - :benchmark => false - :backtrace => false - :bulk_threshold => 1000 - REMOTE SOURCES: - http://gems.rubyforge.org/
2. Install rails. Download from ruby-forge. Link?
gem install -V rails-2.3.3.gem gem install -V mysql
3. Install and configure passenger for Apache2
gem install -V passenger cd /Users/rupert/.gem/ruby/1.8/gems/passenger-2.2.5/bin passenger-install-apache2-module
469 LoadModule passenger_module /Users/rupert/.gem/ruby/1.8/gems/passenger-2.2.5/ext/apache2/mod_passenger.so 470 PassengerRoot /Users/rupert/.gem/ruby/1.8/gems/passenger-2.2.5 471 PassengerRuby /usr/local/bin/ruby 472 473 <VirtualHost *:80> 474 RailsBaseURI /rails/travelsiteph 475 </VirtualHost>
4. Create a sample rails project (“travelsiteph”) in your project directory (“/Users/rupert/projects/rails”).
$ cd /Users/rupert/projects/rails $ rails travelisteph create create app/controllers create app/helpers create app/models create app/views/layouts create config/environments ..... $ ls -la travelsiteph drwxr-xr-x 15 rupert admin 510 2 Sep 21:14 . drwxr-xr-x 5 rupert admin 170 30 Sep 16:31 .. -rw-r--r-- 1 rupert admin 10011 2 Sep 21:14 README -rw-r--r-- 1 rupert admin 307 2 Sep 21:14 Rakefile drwxr-xr-x 6 rupert admin 204 2 Sep 21:14 app drwxr-xr-x 9 rupert admin 306 2 Sep 21:14 config drwxr-xr-x 4 rupert admin 136 2 Sep 21:18 db drwxr-xr-x 3 rupert admin 102 2 Sep 21:14 doc drwxr-xr-x 3 rupert admin 102 2 Sep 21:14 lib drwxr-xr-x 6 rupert admin 204 2 Sep 21:14 log drwxr-xr-x 11 rupert admin 374 2 Sep 21:14 public drwxr-xr-x 11 rupert admin 374 2 Sep 21:14 script drwxr-xr-x 8 rupert admin 272 2 Sep 21:55 test drwxr-xr-x 6 rupert admin 204 2 Sep 22:07 tmp drwxr-xr-x 3 rupert admin 102 2 Sep 21:14 vendor
I have /wwwroot as my document WebRoot. Its running cf, php and mapserv (cgi-bin). Since I want to mix it with rails development, I’ll just make a rails subdirectory. Inside the rails subdirectory, I can create symbolic links to my rails applications located in my projects directory. This way, rails configuration is not exposed from Apache.
cd /wwwroot mkdir rails ln -s /Users/rupert/projects/rails/travelsiteph/public travelsiteph $ ls -la total 8 drwxr-xr-x 3 rupert admin 102 2 Sep 14:09 . drwxrwxr-x 60 root admin 2040 2 Sep 14:08 .. lrwxr-xr-x 1 rupert admin 42 2 Sep 14:09 travelsiteph -> /Users/rupert/projects/rails/travelsiteph/public
5. Restart Apache to take the new configuration
sudo /Library/StartupItems/Apache2/Apache2 restart
6. Open http://127.0.0.1/rails/travelsiteph/
But for development purposes, it is better to use http://127.0.0.1:3000/ to see immediately any changes in code.

Part 2: Rails Development
MySQL Prerequisites:
GRANT ALL PRIVILEGES ON *.* TO rupert@'%' IDENTIFIED BY '*************' WITH GRANT OPTION; $ mysql -u rupert -p
1. Create three databases:
mysql> CREATE DATABASE travelsiteph_development; Query OK, 1 row affected (0.00 sec) mysql> CREATE DATABASE travelsiteph_test; Query OK, 1 row affected (0.00 sec) mysql> CREATE DATABASE travelsiteph_deployment; Query OK, 1 row affected (0.00 sec)
2. Launch Textmate
cd /Users/rupert/projects/rails/travelsiteph mate .
3. Edit database.yml
development: adapter: mysql database: travelsiteph_development username: root password: xxxxxxx host: localhost test: adapter: mysql database: travelsiteph_test username: root password: xxxxxxx host: localhost production: adapter: mysql database: travelsiteph_deployment username: root password: xxxxxxx host: localhost
4. Generate a Poi model. The model should be capitalized and singular.
$ ruby script/generate model Poi exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/poi.rb create test/unit/poi_test.rb create test/fixtures/pois.yml create db/migrate create db/migrate/20090902111538_create_pois.rb
5. Now let’s create the database table for Poi using migrations.
class Poi < ActiveRecord::Migration def self.up create_table :pois, :id => :poi_id do |t| t.column :name, :string t.column :full_address, :string t.column :location, :string t.column :get_there, :string t.column :short_description, :string t.column :full_description, :text t.column :other_info, :text t.column :tel_no, :string t.column :fax_no, :string t.column :mobile_no, :string t.column :email, :string t.column :website, :string t.column :other_contact_details, :text t.column :longitude, :decimal, :precision => 10, :scale => 7 t.column :latitude, :decimal, :precision => 10, :scale => 7 t.column :created_at, :timestamp t.column :updated_at, :timestamp t.timestamps end end def self.down drop_table :pois end end
$ rake db:migrate (in /Users/rupert/projects/rails/travelsiteph) == CreatePois: migrating ========================================= -- create_table(:point_of_interests, {:id=>:poi_id}) -> 0.0353s == CreatePois: migrated (0.0362s) ================================
6. Generate a Poi controller.
$ script/generate controller Poi exists app/controllers/ exists app/helpers/ create app/views/poi create test/functional/ create test/unit/helpers/ create app/controllers/poi_controller.rb create test/functional/poi_controller_test.rb create app/helpers/poi_helper.rb create test/unit/helpers/poi_helper_test.rb
7. Add a list function to Poi Controller
class PoiController < ApplicationController def list @pois = Poi.find(:all) end end
8. Lets test. Open a browser and point to http://127.0.0.1:3000/travelsiteph/poi/list
$ruby script/server
9. Now create the view list.rhtml in views/poi/
<% if @pois.blank? %> <p>There are currently no pois in the system. </p> <% else %> <p>These are the pois in the system: </p> <ul> <% @pois.each do |poi| %> <li><%= link_to poi.name, {:action => 'show', :id => poi.id} -%></li> <% end %> </ul> <% end %>
Part 3: Deploying
1. set RAILS_ENV to production
export RAILS_ENV=production
2. Make sure to populate the database in production mode, run rake db:migrate
3. Capistrano
set :port, 2210 set :application, "halalan2010" #set :repository, "svn+ssh://2rmobile.com/data/repos/web/rails/halalan2010/" set :repository, "http://2rmobile.com/repos/web/rails/halalan2010/" set :scm, :subversion set :scm_username, 'rupert' set :scm_password, proc{Capistrano::CLI.password_prompt('SVN pass:')} # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none` role :web, "2rmobile.com" # IP Your HTTP server, Apache/etc role :app, "2rmobile.com" # This may be the same as your `Web` server role :db, "2rmobile.com", :primary => true # This is where Rails migrations will run #role :db, "your slave db-server here" set :user, "rupert" set :runner, "rupert" set :deploy_to, "/opt/rails/#{application}" # If you are using Passenger mod_rails uncomment this: # if you're still using the script/reapear helper you will need # these http://github.com/rails/irs_process_scripts namespace :deploy do task :start do run "/etc/init.d/apache2 start" end task :stop do run "/etc/init.d/apache2 stop" end task :restart, :roles => :app, :except => { :no_release => true } do run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}" end task :production do run "export RAILS_ENV=production" end end namespace :db do task :seed do run "cd #{deploy_to}/current && RAILS_ENV=production rake db:seed" end task :populate do run "cd #{deploy_to}/current && RAILS_ENV=production rake db:populate" end end
Capistrano commands I normally use:
#on local #cap deploy:setup #on remote and change owner and permissions of project sudo chown -Rf rupert:root halalan2010 #on local #cap deploy #cap db:seed #cap db:populate
Part 4: Miscellaneous
1. Get a description or rake commands
rake -D db
2. How to populate the database in production mode?
rupert:halalan2010 rupert$ export RAILS_ENV=production rupert:halalan2010 rupert$ rake db:migrate (in /Users/rupert/projects/rails/halalan2010) == CreateDatabase: migrating ================================================= -- create_table(:candidates) -> 0.0036s -- create_table(:voters) -> 0.0031s -- create_table(:votes) -> 0.0027s == CreateDatabase: migrated (0.0100s) ========================================
http://blog.airbladesoftware.com/2009/4/10/avoid-typing-rails_env-all-the-time
3. Uninstall specific gem version
gem uninstall activesupport -v 2.2.2
4 Add a source to gem
rupert:rails rupert$ sudo gem sources -a http://gems.github.com http://gems.github.com added to sources
5. Adding a rails project in svn
#create a remote dir svn mkdir http://www.2rmobile.com/repos/web/rails/virginmobilechecker #checkout and copy all files cd ~/projects/rails mv virginmobilechecker virginmobilechecker_old svn co "svn+ssh://2rmobile.com/data/repos/web/rails/virginmobilechecker" virginmobilechecker mv virginmobilechecker_old/* virginmobilechecker/ cd virginmobilechecker svn add * #ignoring log files svn revert log/* svn propset svn:ignore "*.log" log svn propset svn:ignore "*" tmp svn propset svn:ignore "*" doc #commit the files svn commit -m "first commit" *
iPhone Note #20:Integrating Mapserver/TileCache to RouteMe
Jan 14th
Below is a summary of how I was able to implement Mapserver, TileCache and Route-Me iPhone Mapping Framework.
1. Assuming you have a working Mapserver/TileCache setup. Take note of the ff parameters: resolution and bbox. Below is my tilecache.cfg:
61 [mapserver_australia_3857] 62 type=MapServerLayer 63 mapfile=/Users/rupert/projects/pelicancorp/DMOB/trunk/map/australia_3857.map 64 layers=all 65 extension=jpg 66 bbox=-20037508.34, -20037508.34, 20037508.34, 20037508.34 68 maxResolution=156543.033928041 70 levels=20 71 srs=EPSG:3857 72 tms_type=google 73 extent_type=loose 74 spherical_mercator=true
2. Grab the RMGenericMercatorWMSSource from http://groups.google.com/group/route-me-map/browse_thread/thread/b58fa1d20cf15823/e30c42d9c90a8170?lnk=gst&q=Generic#e30c42d9c90a8170
3. By default without any changes, the RMGenericMercatorWMSSource could display Mapserver WMS Tiles. This is possible by passing an NSDictionary *parameters, which contains arrayValues and arrayKeys for creating an http 256×256 image request to the http://127.0.0.1/cgi-bin/mapserv binary.
//This would request to http://127.0.0.1/cgi-bin/mapserv NSArray *arrayValues = [[NSArray alloc] initWithObjects:@"/path-to/australia_3857.map", @"all", @"png", @"EPSG:3857",nil]; NSArray *arrayKeys = [[NSArray alloc] initWithObjects:@"MAP", @"LAYERS", @"FORMAT", @"SRS", nil]; NSDictionary *wmsParameters = [[NSDictionary alloc] initWithObjects: arrayValues forKeys:arrayKeys ];
Resulting http requests to the mapserv binary:
http://192.168.1.193:81/tilecache/tilecache.py?LAYERS=australia_3857&SRS=EPSG:3857&REQUEST=GetMap&SERVICE=WMS&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&FORMAT=png&VERSION=1.1.1&WIDTH=256&HEIGHT=256&BBOX=16828376.000000,-4006523.000000,16833268.000000,-4001631.000000
Note that the bbox values does not contain decimal places. Nevertheless, it still works on Mapserver.
4. Now, assuming we have a valid tilecache running. And it is tested from browser, i.e http://127.0.0.1/map/tilecache_3857.html, open up firebug to see the requests. Below is a sample…
http://192.168.1.193:81/tilecache/tilecache.py?LAYERS=australia_3857&FORMAT=jpg&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A3857&BBOX=16123932.497377,-4539747.9811239,16143500.376618,-4520180.1018829&WIDTH=256&HEIGHT=256
But it seems, route-me is not supplying the bbox values accurately, as the decimal values is truncated (BBOX=16828376.000000,-4006523.000000,16833268.000000,-4001631.000000) from our previous request(3).
After changing the wmsParameter values to create a tilecache request, I noticed that route-me is not displaying the tiles correctly.
//Testing for Windows:TileCache - ? NSArray *arrayValues = [[NSArray alloc] initWithObjects:@"australia_3857", @"png", @"EPSG:3857",nil]; //for WIndows //TileCache URL Parameters: NSArray *arrayKeys = [[NSArray alloc] initWithObjects:@"LAYERS", @"FORMAT", @"SRS", nil];
5. The workaround is to use “double” instead of “floats” in the RMGenericMercatorWMSSource. You can download the zip from RMGenericMercatorWMSSource.zip. I have modified “initialResolution” and “originShift” to both use “double”.
typedef struct { double x; double y; } CGDoublePoint; typedef struct { CGDoublePoint ul; CGDoublePoint lr; } CGXYRect;
Afterwards, I copied the maxResolution and bbox (tilecache) and specified it for the initialResoultion and originShift respectively.
#import "RMGenericMercatorWMSSource.h" CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; CGFloat RadiansToDegrees(CGFloat radians) {return radians * 180/ M_PI;}; @implementation RMGenericMercatorWMSSource -(id) initWithBaseUrl:(NSString *)baseUrl parameters:(NSDictionary *)params { if (![super init]) return nil; // 156543.03392804062 for sideLength 256 pixels // initialResolution = 2 * M_PI * 6378137 / [self tileSideLength]; // specify here whatever the resolution is from tilecache. initialResolution = 156543.033928041; // 20037508.342789244 //originShift = 2 * M_PI * 6378137 / 2; //originShift = 20037508.342789244f; // specify here whatever the bbox is from tilecache. originShift = 20037508.32; NSLog(@"test initialResolution:%f originShift:%f", initialResolution, originShift); // setup default parameters // use official EPSG:3857 by default, user can override to 900913 if needed. wmsParameters = [[NSMutableDictionary alloc] initWithObjects:[[[NSArray alloc] initWithObjects:@"EPSG:3857",@"image/png",@"GetMap",@"1.1.1",@"WMS",nil] autorelease] forKeys:[[[NSArray alloc] initWithObjects:@"SRS",@"FORMAT",@"REQUEST",@"VERSION",@"SERVICE",nil] autorelease]]; [wmsParameters addEntriesFromDictionary:params]; // build WMS request URL template urlTemplate = [NSString stringWithString:baseUrl]; NSEnumerator *e = [wmsParameters keyEnumerator]; NSString *key; NSString *delimiter = @""; while (key = [e nextObject]) { urlTemplate = [urlTemplate stringByAppendingFormat:@"%@%@=%@", delimiter, [[key uppercaseString] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding], [[wmsParameters objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]]; delimiter = @"&"; } int sideLength = [self tileSideLength]; urlTemplate = [[urlTemplate stringByAppendingFormat:@"&WIDTH=%d&HEIGHT=%d",sideLength,sideLength] retain]; return self; } // implement in subclass? -(NSString*) uniqueTilecacheKey { return @"AbstractMercatorWMSSource"; } -(NSString *)shortName { return @"Generic WMS Source"; } -(NSString *)longDescription { return @"Generic WMS Source"; } -(NSString *)shortAttribution { return @"Generic WMS Source"; } -(NSString *)longAttribution { return @"Generic WMS Source"; } -(float) minZoom { return 1.0f; } -(float) maxZoom { return 19.0f; } // Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:3857 -(CGPoint) LatLonToMeters: (CLLocationCoordinate2D) latlon { CGPoint meters; meters.x = latlon.longitude * originShift / 180; meters.y = (log( tan((90.0 + latlon.latitude) * M_PI / 360.0 )) / (M_PI / 180.0)) * originShift / 180; return meters; } //Converts XY point from Spherical Mercator EPSG:3857 to lat/lon in WGS84 Datum -(CLLocationCoordinate2D) MetersToLatLon: (CGPoint) meters { CLLocationCoordinate2D latlon; latlon.longitude = (meters.x / originShift) * 180.0; latlon.latitude = (meters.y / originShift) * 180.0; //latlon.latitude = - 180 / M_PI * (2 * atan( exp( latlon.latitude * M_PI / 180.0)) - M_PI / 2.0); latlon.latitude = 180 / M_PI * (2 * atan( exp( latlon.latitude * M_PI / 180.0)) - M_PI / 2.0); return latlon; } // Converts pixel coordinates in given zoom level of pyramid to EPSG:3857 -(CGDoublePoint) PixelsToMeters: (int) px PixelY:(int)py atZoom:(int)zoom { double resolution = [self ResolutionAtZoom: zoom]; CGDoublePoint meters; double x = (px * resolution - originShift); double y = (py * resolution - originShift); meters.x = x; meters.y = y; NSLog(@"px: %d py: %d resolution: %f originShift: %f x: %f y: %f", px, py, resolution, originShift, x, y); return meters; } -(NSString*) tileURL: (RMTile) tile { //RMLatLongBounds tileBounds = [self TileLatLonBounds:tile]; // Get BBOX coordinates in meters CGXYRect tileBounds = [self TileBounds:tile]; NSString *url = [urlTemplate stringByAppendingFormat:@"&BBOX=%f,%f,%f,%f", tileBounds.ul.x, tileBounds.lr.y, tileBounds.lr.x, tileBounds.ul.y]; NSLog(@"Tile %d,%d,%d yields %@",tile.zoom, tile.x, tile.y, url); return url; } //Returns bounds of the given tile in EPSG:3857 coordinates -(CGXYRect) TileBounds: (RMTile) tile { int sideLength = [self tileSideLength]; int zoom = tile.zoom; long twoToZoom = pow(2,zoom); CGXYRect tileBounds; tileBounds.ul = [self PixelsToMeters: (tile.x * sideLength) PixelY: ((twoToZoom-tile.y) * sideLength) atZoom: zoom ]; tileBounds.lr = [self PixelsToMeters: ((tile.x+1) * sideLength) PixelY: ((twoToZoom-tile.y-1) * sideLength) atZoom: zoom]; return tileBounds; } //Resolution (meters/pixel) for given zoom level (measured at Equator) -(double) ResolutionAtZoom : (int) zoom { return initialResolution / pow(2,zoom); } @end
6. Download TileCache_RouteMe.zip Note: You need a valid mapserver and tilecache running.
iPhone Note #15: Omnigraffle iPhone Stencils, Screen Mockups via PPT, creating an iPhone interface before actual development
Oct 20th

I can not argue enough how valuable an iphone stencil will be in seeing the overall design/flow of an application. I should have done this a long time ago.
Well, I am also one of those eager developers who dives right into coding and see how it goes. But after having the fundamentals of iphone development, objective-c, and app-store submission, I’m taking on a different approach by creating screen mockups mainly for the following reasons:
1. Reach out quickly to the client.
2. Provides a bird’s eye view of the whole application. Provides alternative screens for a better user experience..
3. A follow up on the screen mockups is a powerpoint with actual links to other slides thus mimicing an iphone interface. Note, it is not a complete replica of the simulator but just enough for decision makers to understand what’s in there.

In powerpoint, you can just quickly overlay a transparent polygon on top of the UIButton then attach a hyperlink to another slide.
Hope this post helps you even if it’s not related to coding. On my next post, which is currently buried in my drafts–hopefully it will not be that long, I learned about integrating with Mobile Advertising Networks.
gisnotes down..
Jul 31st
My current blog–gisnotes.com is down at the moment. Its been almost a week and I don’t have much control of the server since moving down under. I would be going back to Beiing (mid-september) and hopefully resurrect my old posts (crossing fingers…)
Anyway, what to expect… I will be blogging mostly in the mobile space as Im currently doing iPhone development.
Comments