Swipe 2011 - iOS Gems
-
Upload
kevin-oneill -
Category
Technology
-
view
1.682 -
download
2
description
Transcript of Swipe 2011 - iOS Gems
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?