Showing posts with label objective-c. Show all posts
Showing posts with label objective-c. Show all posts

Tuesday, February 21, 2012

iOS Custom UISwitch

On an iOS project we had the need to provide a custom UISwitch similar to the following.



With iOSS 5 there is quite a bit of support for interface customization. It's great for customizing almost every control in iOS, except... UISwitch. The only thing customizable with UISwitch is the tint color, which is pretty limited. So, iOS 5 is out for supporting this customization.

Next, we looked at several existing open source projects. There are several out there that support a customized look and feel, but none of these had the "feel" of the built in UISwitch control from Apple. Some would allow dragging, and some just clicking, but none allowed both a click and drag interaction.

There was a strong desire for the switch to behave just like the standard UISwitch that supports both a click and drag interaction. That led us to create our own. We were able to support the look and feel and mimic the behavior of UISwitch by implementing a custom drawRect as well as a custom touch handling. Check the Open Source implementation.

LolayUISwitch.h
LolayUISwitch.m

Next, we hope to extend this to support setting the "on" value with animation, and additionally adding in support for all the autoscale settings of iOS on a UIControl. Also, thoughts of supporting a UIView rather than just a UIImage.

Sunday, December 26, 2010

Disk Caching Protocol Buffers in iOS

There were a few times when we needed to persist Protocol Buffer objects retrieved from the Where U At? servers in order to reduce the frequency of GET calls per client (iPhone/iPad/iPod). You need to gauge your usage and determine when appropriate times are to cache, but for us, photos were an easy win.

If you haven't dabbled in Protocol Buffers for Objective-C, we highly recommend taking a look at http://code.google.com/p/metasyntactic/wiki/ProtocolBuffers for starters.

By doing a bulk photos fetch (for every friend), and persisting to the local iOS documents cache directory, we can save an average of 5-8 calls per user per hour of usage. The following code demonstrates how to convert a Protocol Buffer object to an NSData stream for writing. We simply have these methods within a singleton Objective-C class:

#define USER_CACHE_ALL_PHOTOS_PATH @"All_Photos.pb"

- (NSString*) cacheDir {
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
if ([paths count] > 0) {
return [paths objectAtIndex:0];
}
return nil;
}

- (void)writePhotos {
if (photos != nil) {
NSOutputStream* rawOutput = [[NSOutputStream outputStreamToMemory] retain];
PBCodedOutputStream* output =
[[PBCodedOutputStream streamWithOutputStream:rawOutput] retain];
[photos writeToCodedOutputStream:output];
[output flush];
NSData* actualData = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
NSString* pathToStorePhotos =
[[NSString [self cacheDir], USER_CACHE_ALL_PHOTOS_PATH] retain];
[pathToStorePhotos release];
[output release];
[rawOutput release];
}
}

- (Photos*)readPhotos {
Photos* photosOnDisk = nil;
NSString* pathToStorePhotos = [[NSString
stringWithFormat:@"%@/%@",
[self cacheDir], USER_CACHE_ALL_PHOTOS_PATH] retain];
NSData *photoData = [[NSData dataWithContentsOfFile:pathToStorePhotos] retain];
if (photoData) {
photosOnDisk = [[[[Photos builder] mergeFromData:photoData] build] retain];
}
[photoData release];
[pathToStorePhotos release];
return [photosOnDisk autorelease];
}

Hope you enjoy this little snippet, it can be reused for all your objects that need long-term disk cache.

Friday, December 24, 2010

Objective-C Immutable Example

With Objective-C there is no such thing as truly immutable example as you can always get at the memory and manipulate it (it's just pointers and memory after all). We had a goal of creating an immutable contract for Objective-C but still leverage the @property notation to minimize the memory management errors. The usecase is that in XCode any code type ahead would only show the read only accessors so that we know not to attempt to write to a property. But, internally in the class we can use the synthesized writeable accessors.

Here's an example of how we did it. First, define an interface in a usual manner, but set the properties to @property (readonly). Specify an initializer that takes all the methods.
#import <foundation/foundation.h>

@interface FooItem : NSObject

@property (nonatomic, retain, readonly) NSString* name;
@property (nonatomic, retain, readonly) NSNumber* count;
- (id) initWithValues:(NSString*) givenName withCount:(NSNumber*) givenCount; @end
Then in the implementation define a no name category that specifies the actual properties to be synthesized. These are done as readwrite so that the implementation itself has write accessors. In the initializer still continue to use the self accessors to have the benefits of runtime memory management.
#import "FooItem.h"

@interface FooItem ()

@property (nonatomic, retain, readwrite) NSString* name;
@property (nonatomic, retain, readwrite) NSNumber* count;

@end

@implementation FooItem

@synthesize name;
@synthesize count;

- (id) initWithValues:(NSString*) givenName withCount:(NSNumber*) givenCount {
    self = [super init];
    if (self) {
        self.name = givenName;
        self.count = givenCount;
    }
 
    return self;
}

- (void) dealloc {
    self.name = nil;
    self.count = nil;
 
    [super dealloc];
}

@end
Note, you could go to a true immutable implementation which would do a copy of the objects given in the initializer and specify copy on the @property. That seems an unnecessary amount of overhead for everyday use.