PC Gaming And Console Gaming Will Converge…Soon

xi3-piston-03
This thing is still too slow, hot and expensive

This year, the two worlds of PCs and Consoles will start to converge. Depending on how well Valve executes the Steam Box hardware and content roll out, it may happen almost overnight and with shocking speed.

Game consoles in the current generation (aside from the Wii-U) have been designed to prevent gamers from accessing their games and limit the power of their owners, however, the release of new, high powered, mobile gaming chips including Intel’s Haswell chip and NVidia’s Kepler may prove to be the tipping point into a new realm of high powered, console-like, PC’s.

Here are some laughable characteristics of the current crop of “hardcore” game consoles which leave them ripe for disruption:

  • You can’t play games you bought for past console generations
  • You must subscribe to nonsensical “services” in order to access features like internet browsers, Netflix, Hulu and online gameplay
  • You cant / are limited (aka charged money) in your ability to share games with friends
  • You cant play used games
  • DRM is strictly enforced and requires an “always on” internet connection
  • Indie game developers are suppressed or required to jump through onerous hurdles in order to publish or update games on these platforms
  • There are limited integrations with mobile devices or limited streaming from computer or mobile tablet capabilities
  • Games still cost $70

If the SimCity and Diablo III release debacles are not warning enough, the outcry against the XBox One release is the canary in the coal mine for what is about to happen to the console market: sudden death followed by soaring rebirth.

From what I have so far learned, the Steam Box will have all the positive aspects of PC gaming and none of the negative aspects of console gaming. In one word: Freedom.

 

The Console Market Is Looking Like The Phone Market In 2006

hand-grave

The new XBox reveal event was today.

Basically, it is the exact same as the old XBox except larger, the interface has been made five times more complicated by the inclusion of a range of hand waving and obscure voice commands, it now requires that you have a plugged-in Kinect at all times, won’t play any old XBox 360 games and…something something something TV.Not just any tv…regular, boring network cable television! Oh and lets not forget…it won’t play used games.

After the debut of the PS4 (yet another large black slab device) and the failed Wii-U, it is fairly safe to say that innovation in the console market is now dead and that either Valve or Apple should now feel welcome to steam roll the current field of mediocrity with something new and different.

 

 

Smart Collection View Cells

I was recently introduced to a neat way for UITableViewCells to automatically wire their own XIBs and register themselves with their parent table view. This nicely moves the responsibility for doing this out of table view and into the table view cell itself…makes for a generally cleaner table view controller “.m” class in general, especially when dealing with tables that can have multiple different cell types.

The same is true of UICollectionViewController. Here is a handy class to help do the same:

To use them, just put something like this in –
– (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;

RestaurantCollectionViewCell *cell = [RestaurantCollectionViewCell cellForCollectionView:self.collectionView forIndexPath:indexPath];

@interface SmartCollectionViewCell : UICollectionViewCell

+(NSString*)cellIdentifier;
+(id)cellForCollectionView:(UICollectionView*)collectionView fromNib:(UINib*)nib forIndexPath:(NSIndexPath*)indexPath;
+(id)cellForCollectionView:(UICollectionView*)collectionView forIndexPath:(NSIndexPath*)indexPath;
+(UINib*)nib;

@end

#import "SmartCollectionViewCell.h"

@implementation SmartCollectionViewCell

+(id)cellForCollectionView:(UICollectionView*)collectionView
                   fromNib:(UINib*)nib
              forIndexPath:(NSIndexPath*)indexPath{

    NSString *cellIdentifier = [self cellIdentifier];
    [collectionView registerClass:[self class]
            forCellWithReuseIdentifier:cellIdentifier];
    [collectionView registerNib:nib forCellWithReuseIdentifier:cellIdentifier];
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier
                                                                           forIndexPath:indexPath];
    return cell;
}

+(id)cellForCollectionView:(UICollectionView*)collectionView
              forIndexPath:(NSIndexPath*)indexPath{
    return [[self class] cellForCollectionView:collectionView
                                       fromNib:[self nib]
                                  forIndexPath:indexPath];
}

+ (NSString *)nibName {
    return [self cellIdentifier];
}

+ (NSString *)cellIdentifier {
    [NSException raise:NSInternalInconsistencyException format:@"WARNING: YOU MUST OVERRIDE THIS GETTER IN YOUR CUSTOM CELL .M FILE"];
    static NSString* _cellIdentifier = nil;
    _cellIdentifier = NSStringFromClass([self class]);
    return _cellIdentifier;
}

+(UINib*)nib{
    NSBundle * classBundle = [NSBundle bundleForClass:[self class]];
    UINib * nib = [UINib nibWithNibName:[self nibName]
                                 bundle:classBundle];
    return nib;
}

@end

The Ins and Outs Of AutoLayout

Interface Builder Is A Bad Guesser

Interface Builder attempts to guess which constraints you “meant” to install whenever you move anything around in IB. 60% of the time, XCode guesses wrong….enough to make it cumbersome.

An equally painful problem is attempting to arrange sub-views on top of backing views. For example, in some cases I want a label to be on top of a view but not *in* that view. Interface Builder defaults to automatically sticking your sub-view into the view below it when dragging and dropping things.

Dynamic Layouts Are A Major Pain

Simple struts and springs are vastly easier for laying out interfaces containing many views which can change in size dynamically. Autolayout structures interfere with the process of dynamic layouts in bizarre, often unpredictable ways. Recently I had to put together a table view containing many different labels which could have many lines and needed to update the height of the table cell as well as the contained labels. It was a massive nightmare to do this.

The best you can really do is remove all the height constraints of the individual views in your dynamic layout and then add horizontal width or height distance spacers and then pray that, if the sizes of these views change, the layout will be smart enough to adjust itself.

Hiding Bad Autolayout Constraint Placements

Many times I will hit an Autolayout crash which is directly caused by constraints which don’t actually show up visually in Interface Builder unless I select the parent view and manually examine every constraint until I find the bad one. These hidden ones are often automatically inserted while shifting views around.

Forcing Irritating Constraint Requirements

Massive annoyance I encountered today was that Interface Builder demanded that a “bottom” spacer be inserted below a button. This bottom spacer was ruining the entire layout (inside a scroll view) and causing havoc when attempting to properly size the scroll view. There was no way to get rid of it unfortunately. I wound up scrapping the view controller design and putting it into a custom table view instead.

Adjusting Existing Constraints Is A Huge Pain

Sometimes you might want to adjust a constraint manually. This is generally discouraged, but possible. If Autolayout were a perfect solution, it might be less common. However, in order to manually tweak a constraint, there is no way to simply extract a specific constraint associated with a view….you have to iterate through a flat array of them and then try to scan for the one that interests you. If you want to modify the constraint, in many cases you must destroy it and then replace it in order to change the value.

Useless Crash Messages

Autolayout crashes essentially consist of a large list of constraints which might be the problem ending with one of them being arbitrarily broken in order to stop the application from crashing outright. To me, this is a sign of a fundamentally broken system – it is almost as though the XCode designers / developers threw their hands up in dispair after discovering how AutoLayout was leading to non-stop, impossible to detect in advance crashes and their best solution was to just arbitrarily guess and then snap a given constraint just to make it work.

Finally

In summation: The promise of AutoLayout was a lofty one, but it’s current existing implementation leaves a lot to be desired. I seriously hope this topic is revisited during WWDC and iOS 7.

 

UIImage+GPUImage Category

GPUImage, while a great OpenGL based camera and video library, has its eccentricities. Here is a nice way to easily save an image out of a still camera using a filter.

#import "UIImage+GPUImage.h"

@implementation UIImage (GPUImage)

+(void)imageFromStillCamera:(GPUImageStillCamera*)stillCamera withFilter:(GPUImageFilter*)filter withCompletion:(USPImageFromGPUCameraCompletionBlock)completion andError:(USPImageFromGPUCameraErrorBlock)errorBlock{
     
   // Block for filtering the image.
   void (^handler)(NSData *processedPNG, NSError *error) = ^(NSData *processedPNG, NSError *error) {
       
       UIImage *pngImage = [UIImage imageWithData:processedPNG];
       if(error != nil){
           completion(pngImage);
       }  else {
           errorBlock(error);
       }
   };

   // Process the image.
   [stillCamera capturePhotoAsPNGProcessedUpToFilter:filter
                                    withCompletionHandler:handler];
}

+(void)saveImageFromStillCamera:(GPUImageStillCamera*)stillCamera withFilter:(GPUImageFilter*)filter withCompletion:(USPImageFromGPUCameraCompletionBlock)completion andError:(USPImageFromGPUCameraErrorBlock)errorBlock{
    
    // Block for filtering the image.
    void (^handler)(NSData *processedPNG, NSError *error) = ^(NSData *processedPNG, NSError *error) {
        
        UIImage *pngImage = [UIImage imageWithData:processedPNG];
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        
        NSError *error2 = nil;
        if (![processedPNG writeToFile:[documentsDirectory stringByAppendingPathComponent:@"FilteredPhoto.png"] options:NSAtomicWrite error:&error2])
        {
            completion(pngImage);
        } else{
            errorBlock(error);
        }
    };
    
    // Process the image.
    [stillCamera capturePhotoAsPNGProcessedUpToFilter:filter
                                withCompletionHandler:handler];
}
@end