iOS: Deselecting UITableViewCell with AutoLayout

You'll notice that when deselecting a customized UITableViewCell using tableView-deselectRowAtIndexPath:animated: method inside tableView-didSelectRowAtIndexPath: method is that its contentView gets misplaced. It's probably halfway up the cell. I really think there's a bug on this area for Apple to adress. In the meantime, to work around this bug, you only need to replace the tableView-deselectRowAtIndexPath:animated: method call with tableView-reloadData. Yes it is. And for as long you don't have such a complicated tableView and cell structure, worry not about the performance.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  ... dance right here ...
  [tableView reloadData];
}

UICollectionView: Paging for Smaller Width Cells

In the application I'm working on, there's a 320pt wide horizontal-only scrolling UICollectionView with varied content width depending on the number of items. The UICollectionViewCell subclass or itemView being used here has a width of 250pt enough to let the next itemView peak just a little bit. And I need to show one itemView at a time by snapping to the closest one.

// NOTE: This delegate method requires you to disable UICollectionView's `pagingEnabled` property.
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset {
 
  CGPoint point = *targetContentOffset;  

  UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
 
  // This assumes that the values of `layout.sectionInset.left` and 
  // `layout.sectionInset.right` are the same with `layout.minimumInteritemSpacing`. 
  // Remember that we're trying to snap to one item at a time. So one 
  // visible item comprises of its width plus the left margin.
  CGFloat visibleWidth = layout.minimumInteritemSpacing + layout.itemSize.width;

  // It's either we go forwards or backwards. 
  int indexOfItemToSnap = round(point.x / visibleWidth);

  // The only exemption is the last item. 
  if (indexOfItemToSnap + 1 == [self.collectionView numberOfItemsInSection:0]) { // last item
    *targetContentOffset = CGPointMake(self.collectionView.contentSize.width -
                                       self.collectionView.bounds.size.width, 0);
  } else {
    *targetContentOffset = CGPointMake(indexOfItemToSnap * visibleWidth, 0);
  }
}

Embed Youtube Video using iFrame JS Player API

Here's the code to embed Youtube video using their new iFrame Javascript Player API. This is the piece of code I also used to embed YouTube video in my iOS application. Also, it is possible to play it inline. Not the typical fullscreen playback.


Read more about the iFrame Player API Reference.

It's also worth mentioning that on iOS6 devices, autoplay just doesn't work anymore. Apple added this restriction to save user's bandwidth. Read more about User Control of Downloads Over Cellular Networks.

UPDATE 2014-10-29
- Use this instead: https://developers.google.com/youtube/v3/guides/ios_youtube_helper

iOS: Stopping a chain of Block-based animations

This can easily be done using one line of code and given you're using either [UIView animateWithDuration:animations:] or [UIView animateWithDuration:animations:completion], or both of them interchangeably.

[someView.layer removeAllAnimations]

But what if another variant of these methods is also in the mix? This method is [UIVIew animateWithDuration:delay:options:animations:completion].

What happens is that, whatever is in the completion block of this method, it still is executed right after calling [CALayer removeAllAnimations]. So the right thing to do here is always check for the value of the Boolean argument, finished. If it evaluates to True, then let the rest of the code continue. Otherwise, stop. Also, make sure to include UIViewAnimationOptionAllowUserInteraction in the options parameter.

iOS: Unrecognized selector sent to class...

There are several reasons why this could happen. One of this is when adding a static Objective-C Library into your project. While compiling the app may seem to work fine, the actual code using the library may encounter this exception. The simple solution to this is to add a linker flag -all_load to your project's Targets->YourProject->Build Settings->Linking->Other Linker Flags. 

Read Apple's technical note about this issue here.

IMPORTANT: For 64-bit and iPhone OS applications, there is a linker bug that prevents -ObjC from loading objects files from static libraries that contain only categories and no classes. The workaround is to use the -all_load or -force_load flags. -all_load forces the linker to load all object files from every archive it sees, even those without Objective-C code. -force_load is available in Xcode 3.2 and later. It allows finer grain control of archive loading. Each -force_load option must be followed by a path to an archive, and every object file in that archive will be loaded.


Shared Twitter Application among Developers

Is it possible? Answer is, NO. There's no way you can add multiple developer accounts to an application. According to Twitter, "this is not supported at this time and only one user account can maintain an application record".

Also worth mentioning, it is possible to transfer application's ownership to another user account. Details here.

Effectively render a Drop Shadow on a UIView subclass

Adding a drop shadow to your UIView subclass is as easy as the following code.

UIView *customView = [[UIView alloc] init];
...
customView.layer.shadowColor    = [[UIColor lightGrayColor] CGColor];
customView.layer.shadowOffset   = CGSizeMake(1, 1);
customView.layer.shadowOpacity  = 1.0;
customView.layer.shadowRadius   = 2.0; 
... 

While the common use case of such trick is on rendering stationary views, and no doubt it works perfectly that way, placing them inside a scrollable view like UISrollView or UITableView yields a different feel - it lags a bit when scrolling. So the way to solve this issue and keep them scrolling smoothly is to make use of UIBezierPath. You create an instance of this based on the `bounds` of the receiving view. Then set it as the `shadowPath` of the view's layer object.

UIBezierPath *path  = [UIBezierPath bezierPathWithRect:customView.bounds];
customView.layer.shadowPath = [path CGPath];

Now let's modify our base code to include the bezier path.

UIView *customView = [[UIView alloc] init];
...
UIBezierPath *path  = [UIBezierPath bezierPathWithRect:customView.bounds];
customView.layer.shadowPath = [path CGPath];

customView.layer.shadowColor    = [[UIColor lightGrayColor] CGColor];
customView.layer.shadowOffset   = CGSizeMake(1, 1);
customView.layer.shadowOpacity  = 1.0;
customView.layer.shadowRadius   = 2.0; 
... 

Trouble setting up iOS Push Notifications (APNS)

The following is caused by an invalid or outdated Provisioning Profile.
  • "no valid aps-environment entitlement found for application" (NSError displayed using NSLog)
  • "The entitlements specified in your application’s Code Signing Entitlements file do not match those specified in your provisioning profile" (displayed in Organizer window)
Take note that even if you're just updating an existing App ID in the Provisioning Portal, say you've just enabled this app's Push Notifications, you would also need to modify and re-submit the application's current Provisioning Profile (make sure that your test device is included in the list). After that, download and install this file, update Code Signing Identity section of your project's Build Settings to reflect the new profile (in xcode of course), then you're on to the next case.

Add a Group-By Scope to Yii's CActiveRecord Subclass

Yii Framework allows for named scopes to be added to a CActiveRecord's subclass (you can read more about this here). This also makes it more convenient to filter models based on a predetermined criteria. You can chain them with any filter methods of that class, and ultimately the CDbCriteria methods like `findAll()`.

To site an example, let's have a database table named `animals`. It also has the following fields: id, name, and classification. Let's fill this up with some data. I know this can still be normalized, but let's just leave it like this for the sake of simplicity.

[ 1, 'Eagle',     'Bird']
[ 2, 'Peacock',   'Bird']
[ 3, 'Kangaroo',  'Mammal']
[ 4, 'Dog',       'Mammal']
[ 5, 'Horse',     'Mammal']
[ 6, 'Snake',     'Reptile']
[ 7, 'Turtle',    'Reptile']
[ 8, 'Lizard',    'Reptile']
[ 9, 'Crocodile', 'Reptile']
[10, 'Spider',    'Arthropods']

Now, assuming we're asked to provide a summary of classifications, like return a list of classifications with a number of animals in each of them. This is how our sql query looks like if nothing else is added in the criteria.

SELECT t.classification, COUNT(*) AS animalCount
  FROM `animals` AS t
  GROUP BY t.classification
  ORDER BY animalCount DESC;

If you try to run this query, the result looks something like this.

[Reptile,     4]
[Mammal,      3]
[Bird,        2]
[Arthropods,  1]

Let's assume that we've created a model class named `Animal`, which represents our `animals` db table and has the following method which does the same thing as the aforementioned sql query. It looks like the following:

public function scopes()
{
  return array(
    'groupByClass' => array(
      'group'   => 't.classification',
      'select'  => 't.classification, COUNT(*) as animalCount',
      'order'   => 'animalCount DESC',
    ),
  );
}

This is the overriden method coming from CActiveRecord. To use this, we simply type:

Animal::method()->groupByClass()->findAll();

Then we get the same result:

[Reptile,     4]
[Mammal,      3]
[Bird,        2]
[Arthropods,  1]

You must be wondering how the heck I'm supposed to access `animalCount` alias. Well for that, we can simply add a special property. That would of course be of the same name, which is `animalCount`.

So there you have it.

Facebook IFrame App in Safari

You don't really need cookies to make your Facebook IFrame app work in Safari browser. I remember two years ago, to make Page Tab applications work in Safari, you only need to make use of this trick. I won't be explaining it in details here.

Just recently, I was asked to do this new Fb project that's intended for Page Tab, which now is an iFrame-only platform. I've been developing it using Chrome browser. And when I finally get to test it on Safari, unsurprisingly, it's the same old scenario. What's worse is that the old trick won't work anymore (you better read this).

This app relies so much on Javascript. And instead of deploying several php pages, I only need to come up with a startup page which when loaded on the iFrame, loads the rest of the required scripts. Other pages are dynamically constructed using html fragments that are parsed by a templating script. So most of the Client-Server transactions are done asynchronously. And with Safari continously blocking third-party cookies, it just doesn't work.

After hours of googling for answers, I turned to Facebook's PHP SDK. I found out that it doesn't really require you to use cookies after all (now at 3.1.1). If you take a look at `BaseFacebook` class, particularly the `getSignedRequest` method, you'll see that it first checks the request parameter for Signed Request data (they used to have `session` before this).

...
public function getSignedRequest() {
  if (!$this->signedRequest) {
    if (isset($_REQUEST['signed_request'])) {
      $this->signedRequest = $this->parseSignedRequest(
        $_REQUEST['signed_request']);
    } else if (isset($_COOKIE[$this->getSignedRequestCookieName()])) {
      $this->signedRequest = $this->parseSignedRequest(
        $_COOKIE[$this->getSignedRequestCookieName()]);
    }
  }
  return $this->signedRequest;
}
...

With this in mind, it's possible to send `signed_request` data along with an Ajax request which the PHP SDK can use to determine the user and for your app to verify as well. I use FB.getLoginStatus to acquire a copy of this data. This of course assumes that the user has already authorized your app. Otherwise you'll have to let him do so.

The following example uses jQuery's `$.ajax`.

...
$.ajax({
  url: BACKEND_LINK_HERE,
  type: 'POST',
  data: {
    signed_request: 'sOm3ReA||yLo0ooooooo...oooooooooNgT3xtHeR3',
    another_data: 12345
  }
})
...

So that's it. You need to make sure you have a valid `signed_request` data each time a user interacts with your application. Don't even think about storing it because the access token contained will soon expire which then deems it useless (about 2.5 hours from the moment the user interacted with your app). Yes that's right. And just to let you know, `offline_access` is no longer supported.

Here's a Facebook article about handling invalid and expired access tokens in case you're interested.


Related posts:

Facebook FB.getLoginStatus() Not Working

A lot of them complaining about `FB.getLoginStatus` method not responding. Lucky you, I'm one of them. This is just one of those things common to us when developing an application on Facebook platform. Perhaps the reason why sometimes, changes just seem so hard to adapt. At least only at first.

FB.getLoginStatus(function(response) {
  if (response.status === 'connected') {
    // the user is logged in and has authenticated your
    // app, and response.authResponse supplies
    // the user's ID, a valid access token, a signed
    // request, and the time the access token 
    // and signed request each expire

    var uid = response.authResponse.userID;
    var accessToken = response.authResponse.accessToken;

  } else if (response.status === 'not_authorized') {
    // the user is logged in to Facebook, 
    // but has not authenticated your app

  } else {
    // the user isn't logged in to Facebook.
  }
 });

My application is intended for Page Tab. I'm one of the administrators of it and it's being run on Sandbox Mode.

So here's what I did to solve this issue.



  • go to app's settings page, then Basic tab.
  • fill up `Site URL` field for `Website` under `Select how your app integrates with Facebook` section.
  • hit Save button then reload your app (canvas, page tab,...)

You should see FB.getLoginStatus() in action by now.


Related posts:

Facebook iOS: Use Access Token in PHP

Yes that's right. As of the time of this writing, it is possible to use the user granted access_token back in some server side scripting language like PHP. If you currently have a 255 varchar field size for access_tokens in your database, you may want to bump that up a bit.

The time I made the sample code ran and allowed me to log in (one that came bundled with the Facebook iOS SDK from GitHub), I began realizing that the token quite have different format than the one we usually see in web-based apps (e.g. FB iFrame app).

The usual token looks something like this:

213455681425|1.BGgrgnfWrdpG_X18.3600.1213252135.2-1334679|dHcDbxGbeYbLg3SRgw12fdf4gd60

..while in mobile:

v9ylvkttPnuFWUX4KVdjDPB0SRXkuKX7z281rqjHuG0.eyJpdiI6ImEwWXBDaEtncWpDTU5ibUNuQWdROWcifQ.Y-DwxRY2ZAFZiP7EVuR-HksXqmGw9LXP6umGrfz2XnjSLm0a508u7_jXq0_Kz5a2S8AUUulzUvIRVxTS51_i6VfSByOCbFBIKoBe0-n-Pa8NC29wbuVmGJLvq4W-ezhv0DzA3diiCIqCybt9ELDXoA

The plan was, allow users to connect to our application using FB. Then once he approved it, we take a copy of the access_token and save it back to the server. Same access_token will be used once he logs in to his account using our web-based app.

Well, so far so good. Let me know if yours doesn't work.


Related posts:

Facebook iOS SDK: Let users Connect & Login with FB

Here's how to let your users connect and login to your iOS app using Facebook. The first thing you got to do is download the Facebook iOS SDK. If you have Git installed, you can also do this by pulling from GitHub.

git clone git://github.com/facebook/facebook-ios-sdk.git

Objective-C: Retain, Release, Autorelease

The rules for -retain, -release, and -autorelease are simple. If you alloc, copy, or retain something, you also need to (eventually) release or autorelease it. If you don't do any of those three things, you don't need to do any memory management at all. Not only don't need to, if you try, it messes things up.

If you alloc something, it's just like you sent -retain to the object. So you release or autorelease it. If you do a copy or a mutableCopy, it's also the same as sending a retain. So you also release or autorelease it.

If you never sent a retain (or equivalent) method to an object, then by default it is autoreleased and will go away on its own. The following example doesn't need to be released explicitly since it has already been given an -autorelease message internally. (see NSArray Class Reference -> arrayWithObjects:)
NSArray * sampleList = [NSArray arrayWithObjects:@"One", @"Two", nil];

Also, in my code I always try to release an object instead of autoreleasing it as much as possible.

The actual mechanism here is that when you call -autorelease, a pointer to the object is added to a list kept by an NSAutoreleasePool. When that pool is deallocated, every object in the list gets a -release method. Thus -autorelease is a delayed -release.

Subclassing UIView with NIB file

Here's how UIView subclassing is done (with NIB files). I felt the need of doing so for two reasons: reusability and maintainability.

Header class:
#import <UIKit/UIKit.h>

@interface MyCustomView: UIView

+ (MyCustomView *)createWithOwner:(id)owner;

@end

Implementation class:
#import "MyCustomView.h"

@implementation MyCustomView

+ (MyCustomView *)createWithOwner:(id)owner {
  NSString *nibName = NSStringFromClass([self class]);
  NSArray *nib = [[NSBundle mainBundle] loadNibNamed:nibName owner:owner options:nil];
  MyCustomView *view = [nib objectAtIndex:0];
  return view;
}

@end

Using the the new class is as simple as this:
#import "MyCustomView.h"

...

MyCustomView * customView = [MyCustomView createWithOwner:self];
[self.view addSubView: customView];
[customView release];  

...

Related posts:

Bookmark and Share

Determine if UITableView is near bottom when scrolling

Here's my way of finding out if UITableView is near bottom when scrolling. And when it does, we fetch and load another batch of items.

Facebook `comments.remove` API Method

With FB's Comment Social plugin, having to remove comments on a certain Application is a click away as long as the current user has the Admin rights. This thing right here may be necessary to those who wanted a custom Administration sort of utility for managing FB App related comments.

Here's a sample code of how one could implement this method:

Facebook fb:multi-friend-selector In Page Tab

To those spending days and hours searching for a solution on putting an fb:multi-friend-selector inside a Page Tab, well, sadly you won't find anything out there. The challenge is to make it work inside a page tab. Note that page tab is restricted to 520 pixels wide only. So no matter what you do, you'd still get this dialog box cut off since it's stuck at 580px wide.

In our case, we tried a different approach. We went using window popups. This way we could still display the multi-friend-selector preview dialog in its entirety.

I think fb:multi-friend-selector is only intended in Canvas Page. But that doesn't hinder us now.

Grab the Picture of a Facebook Graph Object

Here's what you need to do to get to the actual location of a Facebook profile picture using PHP cURL (this comes in handy if you plan to store the picture somewhere else). In particular, we can grab the pictures of People, Events, Groups, Pages, Applications, and Photo Albums (these are called objects in Graph API).

A note on GIT's .gitignore

Here's an excerpt from Nick Quaranto's "Ignoring Files" article. I tend to forget this all the time.