swipe 2011 - ios gems

Post on 03-Jul-2015

1.682 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

iOS GemsSome jewels for your glory boxMy talk from Swipe 2011.

TRANSCRIPT

Some jewels for your glory box

Kevin O’NeillDelivery Manager – MobileREA Grouptwitter: @kevinoneill

iOS Gems

This is not a con!dential session — please stream, blog, tweet and take pictures :)

Roadmap

• Enhancing NSArray

• View size and layout

• Simple gestures handling

• Closing thoughts

Enhancing NSArray

NSArrayGreat Core Support

But …

It’s verbose- (NSIndexSet *)indexesOfObjectsWithOptions:(NSEnumerationOptions)opts passingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop)) predicate

Difficult to combine- (NSIndexSet *)indexesOfObjectsAtIndexes:(NSIndexSet *)indexes options:(NSEnumerationOptions)opts passingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate;

We Can Do Better

• Enumeration

• Filtering

• Transformation

Enumeration- (void)each: (void (^)(id item))block;

- (void)eachWithIndex: (void (^)(id, NSUInteger))block;

Consider carefully

Results by side effect only

Huh?

Enumerations produce no ‘value’

They mutate state of the world around them

EGNSMutableSet *uniqueNames = [NSMutableSet set];

[names each: ^ (id name) { [uniqueNames addObject:name];}];

OR[[view subviews] eachWithIndex:^ (id subview, NSUInteger position) {

CGRect cell_frame = CGRectMake(subview_width * position, 0, requested_subview_size.width, requested_subview_size.height);

DemoEnumerationUse and implementation

A for loop may often be a better choice

Useful at the tail of transform operations

Filtering- (NSArray *)filter:(BOOL (^)(id item))block;

- (NSArray *)pick:(BOOL (^)(id item))block;

- (id)first:(BOOL (^)(id))block;

- (id)last:(BOOL (^)(id))block;

Filter removes matching elements

FilterNSArray *names = [NSArray arrayWithObjects:

@"Kevin", @"Sue", @"Aaron", @"Jack", @"Maddie", nil];

[[names filter:^BOOL(id name) { return [name length] < 5;}] each:^(id name) { NSLog(@"%@", name);}];

"Kevin""Aaron""Maddie"

Pick selects matching elements

PickNSArray *names = [NSArray arrayWithObjects: @"Kevin", @"Sue", @"Aaron", @"Jack", @"Maddie", nil];

[[names pick:^BOOL(id name) { return [name length] < 5;}] each:^(id name) { NSLog(@"%@", name);}];

"Sue""Jack"

First returns the first element matched

FirstNSArray *names = [NSArray arrayWithObjects: @"Kevin", @"Sue", @"Aaron", @"Jack", @"Maddie", nil];

NSLog(@"%@", [names first:^BOOL(id name) { return [name length] < 5;}]);

"Sue"

Last returns the last element matched

LastNSArray *names = [NSArray arrayWithObjects: @"Kevin", @"Sue", @"Aaron", @"Jack", @"Maddie", nil];

NSLog(@"%@", [names last:^BOOL(id name) { return [name length] < 5;}]);

"Jack"

DemoFilter, Pick, First and LastUse and implementation

Transformation- (NSArray *)map:(id (^)(id item))block;

- (id)reduce:(id (^)(id current, id item))block initial:(id)initial;

- (NSArray *)intersperse:(id (^) (void))separator;

Map applies the block to each element

MapNSArray *names = [NSArray arrayWithObjects: @"Kevin", @"Sue", @"Aaron", @"Jack", @"Maddie", nil];

[[names map:^id(id name) { return [NSNumber numberWithInteger: [name length]]; }] each:^(id length) { NSLog(@"%@", length);}];

"5""3""5"…

Reduce applies the block to each element passing

the result along

ReduceNSArray *names = [NSArray arrayWithObjects: @"Kevin", @"Sue", @"Aaron", @"Jack", @"Maddie", nil];

NSLog(@"%@",[names reduce:^id(id current, id item) {

NSInteger result = [current integerValue] + [item length]; return [NSNumber numberWithInteger:result];

} initial:[NSNumber numberWithInteger:0]]);

"23"

Place the result of the block between elements

IntersperseNSArray *names = [NSArray arrayWithObjects: @"Kevin", @"Sue", @"Aaron", @"Jack", @"Maddie", nil];

[[names intersperse:^id(id current, id next) { return [current length] > [next length] ? @">" : @"<"; }] each:^(id item) { NSLog(@"%@", item);}];

"Kevin"">""Sue""<"…

DemoMap, Reduce, IntersperseUse and implementation

View size and layout

How to size and layout subviews without pain

I have a confession

I’m an interface builder muppet

Two methods are key

Two methods are key- (void)layoutSubviews;- (CGSize)sizeThatFits:(CGSize)size;

A diversion.Paired methods.

sizeThatFits: and layoutSubviews are loosely

paired

They must be sympathetic to one and other

- (void)layoutSubviews;

• Does nothing by default

• Used to position subviews

• Called when the layout is dirty

• Don’t call it manually– I’ve seen to many times

• Don’t resize self – I’ve seen to many times

- (CGSize)sizeThatFits: (CGSize)size;

• Returns current size by default

• Return ‘best’ size to fit given size

• Doesn’t resize the view

• Don’t resize the view – I’ve seen to many times

• Don’t layout view – I’ve seen to many times

But here’s the rub

The calculations used are often the same, just applied differently

EG- (CGSize)sizeThatFits:(CGSize)size;

{ float width = size.width; float height = [[[self subviews] reduce: ^ id (id current, id item) { CGSize item_size = [item sizeThatFits:CGSizeMake(width, 0.)];

return [NSNumber numberWithFloat: ceilf([current floatValue] + (item_size.height + [self spacingForSubview:item]))];

} initial:[NSNumber numberWithFloat:0.]] floatValue];

CGSize result = CGSizeMake(width, height); return result;}

And- (void)layoutSubviews;

{ float width = [self width];

__block StackedView *block_self = self;

[[self subviews] reduce: ^ id (id current, id item) { CGSize item_size = [item sizeThatFits:CGSizeMake(width, 0.)];

[item setFrame: CGRectMake(0, [current floatValue] + [block_self spacingForSubview:item], item_size.width, item_size.height)];

return [NSNumber numberWithFloat:ceilf([item bottom])]; } initial:[NSNumber numberWithFloat:0.]];}

The only real variance here is the action

And that’s a simple example

We can do better

DemoSize and Layout

The layout algorithm is coded once

Then applied appropriately

Simple gestures handling

Gesture recognisers rock

But maintaining pairing between selectors and

actions is a little tedious

EG- (void)cancelRequest

{ [self displayCancelMessage];}

UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cancelRequest)];

We can do better

Can you guess what makes gesture handling easier?

DemoGesture recognisers

Blocks make gesture setup much easier

Closing thoughts

Take from this what you will

Understanding blocks will make you more productive

New Core API’s are taking advantage of blocks

So should you

Categories are a key method of partitioning

behaviour

Blocks are a key method of partitioning algorithms

Associated objects should be part of your toolkit

But

Don’t use these tools because they are there

Use them to make your code …

Simpler

Easier to maintain

https://github.com/kevinoneill/Useful-Bitshttps://github.com/kevinoneill/Useful-Swipehttps://github.com/domesticcatsoftware/DCIntrospect

Open source libraries used

Useful Bits

Questions?

top related