Getting UILabel's Text Height with adjustsFontSizeToFitWidth

Working on the 1.1 release of Cee-lo today I ran into a task where I needed to position a button underneath a text field. I wanted said button to be positioned relative to the height of the text field. In Actionscript, this task is easy:

theButton.y = theTextField.y + theTextField.textHeight;

+1 abstracted language, no? In objective-c/iOS SDK, the task is slightly more complex. Especially when the UILabel's font size is set to auto adjust (adjustsFontSizeToFitWidth) based on the width of the text field. Here's the working solution below. In this example, the username field on Cee-lo's winner screen is set and the rematch button is positioned under the UILabel based on the text height of the username field. The max font size for the field is 36 and the minimum is 12.

- (void)setUsernameText:(NSString *)theText
{
    // assign the text to the username field
    self.username.text = theText;

    // actualFontSize accepts a pointer to a CGFloat, so define that here
    CGFloat fontSize;

    // determine the size of the field if we were to show theText
    // in it at the original size
    CGSize size = [theText sizeWithFont:self.username.font 
                            minFontSize:12.0f 
                         actualFontSize:&fontSize 
                               forWidth:self.username.frame.size.width
                          lineBreakMode:self.username.lineBreakMode];
    // so at this point, size is a frame that defines the bounds
    // of the textfield if it were rendered at 36 pt. But now, by reference, fontSize
    // contains the actual size of the text field's font after resizing. 
    // So we use that size to determine how big the field would be
    // with that font size:
    CGSize computed = [theText sizeWithFont:[UIFont fontWithName:HelveticaNeueBold size:fontSize]];

    // position the rematch button under the text field
    self.rematch.frame = CGRectMake(20.0f, self.username.frame.origin.y + computed.height + 5, self.rematch.frame.size.width, self.rematch.frame.size.height);
}
September 19th, 2010 | Permalink

Dear Taco Time,

This "Open Letter To All Of Advertising And Marketing" (in which the author chastises "agencies and marketing" for assuming he had nothing better to do with his time than submit videos about sausages on a microsite) made me laugh a few weeks ago. As the developer of many a microsite, there's been times I've stopped to think "why would anyone do [upload/create/record/submit] to this garbage?" Much of ad agency work feels like a giant creative circle-jerk sometimes, but I think we can all point to recent success of the Old Spice campaign to find an example of truly engaging consumers to respond to a brand in a meaningful way.

That being said, as much as I do not care for crowd-sourcing campaigns, they are not as worthless as this fictional character in the open letter might indicate. It's all about rewarding participants for their creativity. Take my wife's recent (not open) letter to Taco Time into consideration:

Dear Taco Time,

The Taco Time on Elliot is one of my faves in Seattle. My first job was with TT in Spokane on Grand Ave about ... 15 years ago and I have been a religious patron of Taco Time ever since! I joke with my husband that the house that is for sale on 45th Ave (in Seattle) next to the Wallingford Taco Time was meant to be my house :) I love TT so much I want to have you as a neighbor.

Thanks for the great food. I think you should open a few in Albuquerque, NM where I went to college and missed Taco Time so much I almost had to have it Fed Ex'ed to me, which I would have done had I thought the food would be any good when it arrived.

Please consider bringing back your old Nachos recipe which was basically your uber fresh tortilla chips with shredded cheddar cheese, meat, beans, REAL sour cream and ESPECIALLY the enchilada sauce you used to make which was AMAZING and I totally miss it. That and the real sour cream too come to think of it. Your stuff was amazing with full fat sour cream :) If you cannot bring back the enchilada sauce from ten years ago, may I have the recipe?

Sincerely,

Bridget - your number one fan.

Think Bridget would make a video for some free Taco Time?

August 30th, 2010 | Permalink

Street Ceelo: Fun Times with the iPhone SDK

Do you follow me on Twitter? You don't? Well you should! If you do though you know how much pain the iPhone SDK has brought me in the last three or four months. I'm very close to being done with a very cool app, but some Apple bugs are making it quite difficult to get it done. Care to read my story and learn about the iPhone application I'm making? Okay...

ceelo1 ceelo3

The Street Ceelo App

I'm working on a dice game – a street dice game called Ceelo. The application itself is essentially a 3D dice rolling simulation. You can play with one phone, passing it back and forth, or you can connect using iOS's GameKit over bluetooth or wi-fi and play with multiple "iDevices". It features custom UI design and elements, networking, peer to peer gameplay, animation, navigation and tab controllers...basically "the works" of the iOS kit. I used this application as a learning exercise and it's turned into an application I'm very proud of.

Pretty cool huh? Well, it was cool until iOS 4.0 came out. You see, 4.0 introduced some new classes called GameCenter that expand on GameKit. It also broke the hell out of the original GameKit classes. So my application runs "flawlessly" (it's essentially complete) on devices running the iOS versions 3.0–3.1.3, but on 4.0.X there are numerous issues with the GameKit in Apple's underlying SDK and in certain places it crashes. This is not acceptable to me so I am working to rewrite much of the code and some of the user experience itself to accommodate the issues.

The Broken

So, what broke when 4.0 was released? Here's an example. In Ceelo, there's a custom peer to peer table display. One player (server) says they are going to start a game and others (clients) join her. Now, if a client quits at any time, a "message" is send to their peers that they disconnected. No problem; we simply remove them from the list of players (if in the game lobby) or the game ends. This works fantastically in 3.x iOS. Now, in 4.0.x, instead of a quick notification, there's a lengthy delay when a player disconnects or cancels a connection request. When the message is finally "sent" (it isn't really), it crashes the server:

Thread 3 Crashed:
0   GameKitServices                 0x06352f90 gckSessionChangeStateCList + 411
1   GameKitServices                 0x0635b49c gckSessionRecvProc + 1474
2   libSystem.B.dylib               0x981c181d _pthread_start + 345
3   libSystem.B.dylib               0x981c16a2 thread_start + 34

For more information on this crash check out my StackOverflow question on the topic or poke into the developer forums. Radar IDs for these issues include 8095838 & 8203566, 8174249 & 8094858.

So I filed a bug with Apple and it's been confirmed as a "known issue". The great thing is that the recent betas of the SDK fix the crashing. Unfortunately the long delays remain. I'm continuing to file a bug with each release of the beta. I'm really shocked that this hasn't been addressed yet as the 4.1 release is supposed to be the GameCenter/GameKit release. Classic Apple though to release broken product to get it to market. It is what it is and I've learned to be patient with them in becoming an "Objective-C developer". Also, from what I understand, the 3.0 release was very much the same way.

The Fixes

At this point I'm changing some of the connection logic to remove some of the opportunities for players to quit the game. I've also changed the peer to peer game selection interface to just auto-accept incoming connection requests. This was thanks to a brilliant suggestion by Avi Itskovich who diagnosed a similar GameKit bug via my Stackoverflow post on the topic. It seems to work much better if you connect people up first.

If the game ever gets to the store, it'll be interesting to see how I'll word the required iOS version. You see, this game will work on *all* iDevices, even those without Bluetooth (I wrote it so there's wifi fallbacks). It works on iPhone, 3G, 3GS, Touches, etc. From iOS 3.0–3.1.3. It'll probably run very well on iPhone4 with 4.1 when that is released (already upscaled all the graphics for the retina display!). The only OS it won't run well on is 4.0. How should I word that? "We don't support 4.0 at all but everything else is grand". Just seems odd to have support for everything all the way back to 3.0, but here in the middle is this turd. Such is life.

That's Nice...

So what's the point of all this? Well, I'm worried that GameKit may be phased out. It doesn't seem like Apple is putting priority on fixing it and is focusing more on the Xbox Live-like GameCenter (which obviously is much more lucrative prospect than peer-to-peer gaming). So, I wanted to share my experience as an introduction to Typeoneerror Studios offering iOS development one of our new services. As frustrating as this has been, I absolutely love the Apple development kits, tools and the overall process.

Want to beta test at some point?

Send an email to ceelo at typeonerror with your device ID. How do you get that? Use this fantastic video that Sam Vermette hosts at http://whatsmyudid.com/.

August 24th, 2010 | Permalink

Permanent Facebook Sessions and Posting as a Page

For a current project, we are integrating a site with Facebook Connect and linking users of our T1EOS (our Content Management System) to their accounts. When administrators create content, the CMS publishes the article to their brand page directly from the CMS behind the scenes. This was a bit tricky to figure out how to accomplish (wasn't sure it was possible at first) given that the Facebook API had pretty much completely changed from the last time I used it; Facebook now uses OAuth 2.0 for authentication and authorization. So this article then will be an overview of how to get a permanent session (or access_token as it's called on Facebook) that will allow you to post to your fan page as the page (and not your user account).

To start, you must be an adminstrator of the page you want to post as. We're going to essentially be posting from the Application to the Page, but to post from a Facebook Application, you must give the application permission to post to your wall and also permission for offline access. In our case, offline access allows the CMS to process and post content even when I am not logged in. Once you've give permission for the application to post for you, it should be able to post to any pages you are a full administrator of. When you do it like this the post will appear as written if by the page, not your user account.

Luckily the client I am working for was gracious enough to mark me an administrator of their page, but I first tested with Typeoneerror's Fan Page. Go to your own fan page and note the ID in the url for later (hint: it comes after ?id=).

Next, you need to create a Facebook Application. If you haven't yet, you'll need to install the Facebook Developer application. Create an application and give it a name. Note your API Key, Application ID, and Application Secret. The only other thing you should need to change is your Connect URL and Base Domain. Let's just say for example's sake that we'll use http://mydomain.dev/ and mydomain.dev respectively.

Next, you need to authorize your application with some extended permissions. This is quite simple, just point a browser to the following URL, replacing {CLIENT_ID} with your Application ID and {YOUR_DOMAIN} with your Connect URL (note that your URL must have a trailing slash or it may error):

https://graph.facebook.com/oauth/authorize?client_id={CLIENT_ID}&scope=offline_access,publish_stream,create_event,rsvp_event,sms,manage_pages&redirect_uri={YOUR_DOMAIN}

Note the scope parameter. Here we are sending the extended permissions the app wants. If this is your first time authenticating, you'll see a nicely designed window explaining what the app is requesting access to. For our purposes, all we need is offline_access and publish_stream. Offline access will give us a permanent access key, allowing us to make calls to the API without being logged in or authenticated. Publish stream allows the app to, well, publish to your streams.

After you authenticate, you'll be redirected back to YOUR_DOMAIN but now you'll see a query param added to the URL which will look something like:

http://mydomain.dev/?code=1234567890abcdef.

The actual code will be much longer ;) Note it down for later. This is our authentication code that will allow us to get a permanent key. This is not the permanent key quite yet. So how do we get the permanent key? Request one from the access_token service:

https://graph.facebook.com/oauth/access_token?client_id={CLIENT_ID}&redirect_uri={YOU_DOMAIN}&client_secret={APPLICATION_SECRET}&code={YOUR_CODE}

Fire off a cURL request to the above URL, replacing {CLIENT_ID} with your Application ID, {YOUR_DOMAIN} with your Connect URL (make sure it has a trailing slash), {APPLICATION_SECRET} with your Application's Secret Key and {YOUR_CODE} with the code from the previous step. The result should be simply a piece of text that looks like this:

access_token=1234567890abcdef.

Your permanent session access token! Hooray. So let's use it. I'm using the Zend Framework so you'll see some logging includes in this code. You can ignore them. Please read the comments as I noted some tutorial notes in there. Here's the PHP snippet I wrote to post to my Page's wall from an external website. With luck, change the settings noted below and a post will appear on your page's wall too!

require_once "../library/Typeoneerror/Facebook/Library/facebook.php";
require_once "../library/Zend/Debug.php";

// need to turn these options off otherwise
// you will get errors from the API
Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = 0;
Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = 0;

// Replace with your page's ID
$pageId = "YOUR_PAGE_ID";
// Replace with your permanent session key from the last step
$permSess = "YOUR_PERMANENT_ACCESS_KEY";

// create the Facebook API
$facebook = new Facebook(array(
"appId" => "YOUR_APPLICATION_ID",
"secret" => "YOUR_APPLICATION_SECRET",
"cookie" => true,
));

// should log out your page's info.
// simply makes a call to the Graph API
// you don't need a session for this.
$page = $facebook->api("/{$pageId}");
Zend_Debug::dump($page);

// publish to the page
// you do need a session for this.
$rest = $facebook->api(array(
"uid" => $pageId,
"method" => "stream.publish",
"access_token" => $permSess,
"message" => "This is a test. Experimentation with OAuth and permanent sessions on Facebook.",
));

// should log an ID of a created wall post
Zend_Debug::dump($rest);

Did you like this article? Maybe you'll like our company page:

June 29th, 2010 | Permalink

Install and Deploy Django with MAMP using mod_wsgi

Getting Apache ready for serving Python applications seems to really be a headache. mod_python seems relatively easy, but the preferred mod_wsgi seems to give everyone headaches. I wanted to serve my python applications using MAMP since it's part of my daily workflow. I found a decent solution that is working really well for me with MAMP on OSX (Leopard). You may want to check out my previous post on installing and managing a basic OSX Django Stack before reading this one.

Installing WSGI

To install WSGI, you need to compile it against the version of Apache you'll be using it with. MAMP's current packaged version of Apache is 2.0.63, which is not super new, but will do. If you're like me and you just installed the MAMP binary instead of downloading the source code...well, then you don't have the Apache source code for 2.0.63, do you? So, let's get it!

I had already installed Macports in my previous adventure with Django installs. I decided to see what was available Apache-wise, and lo-and-behold:

$ port search "apache"
apache20 @2.0.63 (www)
    The extremely popular second version of the Apache http server

The exact same version that MAMP is packaged with! Awesome! Let's install it...

$ port install apache20

That'll install Apache in /opt/local/apache20 (or wherever Macports is installing stuff on your machine). Next you'll need to grab the mod_wsgi source from author Graham Dumpleton (thanks, Graham!). Unpack that tar.gz file and fire up terminal and cd into that folder. First read this code section:

mod_wsgi-3.2 $ ./configure

checking for apxs2... no
checking for apxs... /usr/sbin/apxs
checking Apache version... 2.2.14
checking for python... /Library/Frameworks/Python.framework/Versions/2.6/bin/python
configure: creating ./config.status
config.status: creating Makefile

So our basic configure checks for apxs (apache extension tool) and finds the default OSX install – which for me is currently 2.2.14; too new for MAMP. So go ahead and add the --with-apxs flag to the configure and point to wherever Macports installed your apache20:

mod_wsgi-3.2 $ ./configure --with-apxs=/opt/local/apache20/bin/apxs
checking Apache version... 2.0.63
checking for python... /Library/Frameworks/Python.framework/Versions/2.6/bin/python
configure: creating ./config.status
config.status: creating Makefile

Ok, so the Apache version looks right this time. Let's make and install that guy:

$ make
$ sudo make install
chmod 755 /opt/local/apache20/modules/mod_wsgi.so

Cool, so looks like it copied the compiled wsgi module into our apache20 installation. Now we just need to copy it to /Applications/MAMP/Library/modules (your MAMP modules folder).

Fire up MAMP Pro and go to File > Edit Template > Apache httpd.conf. Add the following after the other LoadModule directives:

LoadModule wsgi_module modules/mod_wsgi.so

Ok, that's the install. Stop and start MAMP services and you've got WSGI ready to serve python apps. Next step is to configure a Django site to be served.

Using WSGI

To start, I've commited my "learning" code to github, so you can check out how I'm learning how to set up and use Django on my machine. I'll be referencing these files throughout so grab them if you want them. I'm also assuming you've created a basic Django project and an application. My learning code uses the same code from the introduction tutorial on the djangoproject.com site. My sample code is in a directory called "project" and I have an application called "polls."

First thing you want to do is create a named host like you always do with MAMP. Switch to the hosts panel and hit the "+" button. I called this one "django.dev". In the General settings, point the document root to your project's root; in my case it points to /Library/WebServer/Documents/Django/project.

Now in your django project, create a folder called "apache" and create two files there.

/project/
    /apache/
        apache_django_wsgi.conf
        django.wsgi
    settings.py
    polls
    ...

See the two files and folder stucture on github here. django.wsgi is your primary wsgi application. This is mounted as the root of your website:

import os, sys

# path to parent folder of project
sys.path.append('/Library/Webserver/Documents/Django')
# path to your settings module
os.environ['DJANGO_SETTINGS_MODULE'] = 'project.settings'

# create a wsgi "application"
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

You can see my setup here. apache_django_wsgi.conf is the httpd.conf file for your VirtualHost:

Alias /static /Library/Webserver/Documents/Django/project/static

<Directory /Library/Webserver/Documents/Django/project/media>
Order deny,allow
Allow from all
</Directory>

WSGIScriptAlias / /Library/Webserver/Documents/Django/project/apache/django.wsgi

You can see that mine defines an alias that points to a static folder (where I'll serve my js, css, images from for now) and a WSGIScriptAlias which is the full path to my .wsgi application.

Now back in MAMP, go to the advanced settings of your virtual host and in the "Customized virtual host general settings" add:

Include /Library/Webserver/Documents/Django/project/apache/apache_django_wsgi.conf

This would be the full path to your .conf file in your project. Again, restart MAMP's services. So now when you hit django.dev, the application defined in your .wsgi will be served. django.dev/static/ will be avaiable for you to add static content like js, css, etc. The end.

May 16th, 2010 | Permalink
prev 2 3 4 5 6 7 8 9 10 11 next