Wednesday, June 26, 2013

How To Make Fast Scrolling UITableViews With Custom Section Headers and Custom UITableViewCells




1. Create a new master detail application in XCode.

2. To set a custom flat navigation bar color create a two new objective-c categories with the code below and configure the navigation bar by importing the UINavigationBar+FlatUI.h file into your master view controller and calling configureFlatNavigationBarWithColor: on your navigation bar in the viewDidLoad method. You should end up with something like below.




// At the Top of the file
#import "UINavigationBar+FlatUI.h"

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
[self.navigationController.navigationBar configureFlatNavigationBarWithColor:[UIColor colorWithRed:44.0f/255.0f green:71.0f/255.0f blue:98.0f/255.0f alpha:1.0f]];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
}

UINavigationBar+FlatUI.h


//
// UINavigationBar+FlatUI.h
// FlatUI
//
// Created by Jack Flintermann on 5/3/13.
// Copyright (c) 2013 Jack Flintermann. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UINavigationBar (FlatUI)

- (void) configureFlatNavigationBarWithColor:(UIColor *)color; UI_APPEARANCE_SELECTOR

@end

UINavigationBar+FlatUI.m


//
// UINavigationBar+FlatUI.m
// FlatUI
//
// Created by Jack Flintermann on 5/3/13.
// Copyright (c) 2013 Jack Flintermann. All rights reserved.
//

#import "UINavigationBar+FlatUI.h"
#import "UIImage+FlatUI.h"

@implementation UINavigationBar (FlatUI)

- (void) configureFlatNavigationBarWithColor:(UIColor *)color {
[self setBackgroundImage:[UIImage imageWithColor:color cornerRadius:0]
forBarMetrics:UIBarMetricsDefault & UIBarMetricsLandscapePhone];
NSMutableDictionary *titleTextAttributes = [[self titleTextAttributes] mutableCopy];
if (!titleTextAttributes) {
titleTextAttributes = [NSMutableDictionary dictionary];
}
[titleTextAttributes setValue:[UIColor clearColor] forKey:UITextAttributeTextShadowColor];
[titleTextAttributes setValue:[NSValue valueWithUIOffset:UIOffsetMake(0, 0)] forKey:UITextAttributeTextShadowOffset];
[self setTitleTextAttributes:titleTextAttributes];
if([self respondsToSelector:@selector(setShadowImage:)])
{
[self setShadowImage:[UIImage imageWithColor:[UIColor clearColor] cornerRadius:0]];
}
}

@end

UIImage+Color.h


//
// UIImage+Color.h
// FlatUI
//
// Created by Jack Flintermann on 5/3/13.
// Copyright (c) 2013 Jack Flintermann. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UIImage (FlatUI)

+ (UIImage *)imageWithColor:(UIColor *)color
cornerRadius:(CGFloat)cornerRadius;

+ (UIImage *) backButtonImageWithColor:(UIColor *)color
barMetrics:(UIBarMetrics) metrics
cornerRadius:(CGFloat)cornerRadius;

UIImage+Color.m


+ (UIImage *)imageWithColor:(UIColor *)color
cornerRadius:(CGFloat)cornerRadius {
CGFloat minEdgeSize = edgeSizeFromCornerRadius(cornerRadius);
CGRect rect = CGRectMake(0, 0, minEdgeSize, minEdgeSize);
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];
roundedRect.lineWidth = 0;
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0f);
[color setFill];
[roundedRect fill];
[roundedRect stroke];
[roundedRect addClip];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return [image resizableImageWithCapInsets:UIEdgeInsetsMake(cornerRadius, cornerRadius, cornerRadius, cornerRadius)];
}

+ (UIImage *) backButtonImageWithColor:(UIColor *)color
barMetrics:(UIBarMetrics) metrics
cornerRadius:(CGFloat)cornerRadius {

CGSize size;
if (metrics == UIBarMetricsDefault) {
size = CGSizeMake(50, 30);
}
else {
size = CGSizeMake(60, 23);
}
UIBezierPath *path = [self bezierPathForBackButtonInRect:CGRectMake(0, 0, size.width, size.height) cornerRadius:cornerRadius];
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
[color setFill];
[path addClip];
[path fill];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return [image resizableImageWithCapInsets:UIEdgeInsetsMake(cornerRadius, 15, cornerRadius, cornerRadius)];

}

3. Now to make the navigation items look flat we can create another objective-c category to change the appearance. Import UIBarButtonItem+FlatUI.h in your master view controller Then add the code below to the viewDidLoad method. You should see something like this:



- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
[self.navigationController.navigationBar configureFlatNavigationBarWithColor:[UIColor colorWithRed:44.0f/255.0f green:71.0f/255.0f blue:98.0f/255.0f alpha:1.0f]];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
[UIBarButtonItem configureFlatButtonsWithColor:[UIColor colorWithRed:52.0f/255.0f green:152.0f/255.0f blue:219.0f/255.0f alpha:1.0f]
highlightedColor:[UIColor colorWithRed:41.0f/255 green:128.0f/255 blue:185.0f/255 alpha:1.0f]
cornerRadius:3];
}

UIBarButtonItem+FlatUI.h


//
// UIBarButtonItem+FlatUI.h
// FlatUI
//
// Created by Jack Flintermann on 5/8/13.
// Copyright (c) 2013 Jack Flintermann. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UIBarButtonItem (FlatUI)

// styles a single bar button item
- (void) configureFlatButtonWithColor:(UIColor *)color
highlightedColor:(UIColor *)highlightedColor
cornerRadius:(CGFloat) cornerRadius UI_APPEARANCE_SELECTOR;

// styles all bar button items that exist within a class heirarchy (same as UIAppearanceProxy methods)
+ (void) configureFlatButtonsWithColor:(UIColor *) color
highlightedColor:(UIColor *)highlightedColor
cornerRadius:(CGFloat) cornerRadius
whenContainedIn:(Class )containerClass, ... NS_REQUIRES_NIL_TERMINATION;

// styles all bar button items (can be overwritten with the above methods)
+ (void) configureFlatButtonsWithColor:(UIColor *) color
highlightedColor:(UIColor *)highlightedColor
cornerRadius:(CGFloat) cornerRadius;


// removes the text shadows off a single bar button item (sadly, this can't be easily done for all buttons simultaneously)
- (void) removeTitleShadow;

@end

UIBarButtonItem+FlatUI.m


//
// UIBarButtonItem+FlatUI.m
// FlatUI
//
// Created by Jack Flintermann on 5/8/13.
// Copyright (c) 2013 Jack Flintermann. All rights reserved.
//

#import "UIBarButtonItem+FlatUI.h"
#import "UIImage+FlatUI.h"

@implementation UIBarButtonItem (FlatUI)

- (void) configureFlatButtonWithColor:(UIColor *)color
highlightedColor:(UIColor *)highlightedColor
cornerRadius:(CGFloat) cornerRadius {

[UIBarButtonItem configureItemOrProxy:self forFlatButtonWithColor:color highlightedColor:highlightedColor cornerRadius:cornerRadius];

}

+ (void) configureFlatButtonsWithColor:(UIColor *) color
highlightedColor:(UIColor *)highlightedColor
cornerRadius:(CGFloat) cornerRadius {

[self configureFlatButtonsWithColor:color highlightedColor:highlightedColor cornerRadius:cornerRadius whenContainedIn:[UINavigationBar class], [UINavigationController class], nil];
}

+ (void) configureFlatButtonsWithColor:(UIColor *) color
highlightedColor:(UIColor *)highlightedColor
cornerRadius:(CGFloat) cornerRadius
whenContainedIn:(Class )containerClass, ... {
va_list vl;
va_start(vl, containerClass);
id appearance = [UIBarButtonItem appearanceWhenContainedIn:containerClass, nil];
va_end(vl);
[UIBarButtonItem configureItemOrProxy:appearance forFlatButtonWithColor:color highlightedColor:highlightedColor cornerRadius:cornerRadius];
}

- (void) removeTitleShadow {
NSMutableDictionary *titleTextAttributes = [[self titleTextAttributesForState:UIControlStateNormal] mutableCopy];
if (!titleTextAttributes) {
titleTextAttributes = [NSMutableDictionary dictionary];
}
[titleTextAttributes setValue:[NSValue valueWithUIOffset:UIOffsetMake(0, 0)] forKey:UITextAttributeTextShadowOffset];
[self setTitleTextAttributes:titleTextAttributes forState:UIControlStateNormal];
}

//helper method, basically a wrapper to allow creating a custom UIAppearance method that doesn't conform to the usual naming style
+ (void) configureItemOrProxy:(id)appearance
forFlatButtonWithColor:(UIColor *)color
highlightedColor:(UIColor *)highlightedColor
cornerRadius:(CGFloat) cornerRadius {
UIImage *backButtonPortraitImage = [UIImage backButtonImageWithColor:color
barMetrics:UIBarMetricsDefault
cornerRadius:cornerRadius];
UIImage *highlightedBackButtonPortraitImage = [UIImage backButtonImageWithColor:highlightedColor
barMetrics:UIBarMetricsDefault
cornerRadius:cornerRadius];
UIImage *backButtonLandscapeImage = [UIImage backButtonImageWithColor:color
barMetrics:UIBarMetricsLandscapePhone
cornerRadius:2];
UIImage *highlightedBackButtonLandscapeImage = [UIImage backButtonImageWithColor:highlightedColor
barMetrics:UIBarMetricsLandscapePhone
cornerRadius:2];

[appearance setBackButtonBackgroundImage:backButtonPortraitImage
forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
[appearance setBackButtonBackgroundImage:backButtonLandscapeImage
forState:UIControlStateNormal
barMetrics:UIBarMetricsLandscapePhone];
[appearance setBackButtonBackgroundImage:highlightedBackButtonPortraitImage
forState:UIControlStateHighlighted
barMetrics:UIBarMetricsDefault];
[appearance setBackButtonBackgroundImage:highlightedBackButtonLandscapeImage
forState:UIControlStateHighlighted
barMetrics:UIBarMetricsLandscapePhone];

[appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(1.0f, 1.0f) forBarMetrics:UIBarMetricsDefault];
[appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(1.0f, 1.0f) forBarMetrics:UIBarMetricsLandscapePhone];

UIImage *buttonImageNormal = [UIImage imageWithColor:color cornerRadius:cornerRadius];
UIImage *buttonImageHightlighted = [UIImage imageWithColor:highlightedColor cornerRadius:cornerRadius];
[appearance setBackgroundImage:buttonImageNormal forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[appearance setBackgroundImage:buttonImageHightlighted forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
}

@end
4. To create custom section header views in your tableview create a new section header view class and add a new category and add the code below to your master view controller. Also add the QuartzCore.Framework to your project. You should end up with something like this.








- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
[self.navigationController.navigationBar configureFlatNavigationBarWithColor:[UIColor colorWithRed:44.0f/255.0f green:71.0f/255.0f blue:98.0f/255.0f alpha:1.0f]];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
[UIBarButtonItem configureFlatButtonsWithColor:[UIColor colorWithRed:52.0f/255.0f green:152.0f/255.0f blue:219.0f/255.0f alpha:1.0f]
highlightedColor:[UIColor colorWithRed:41.0f/255 green:128.0f/255 blue:185.0f/255 alpha:1.0f]
cornerRadius:3];
// Add This Line
self.tableView.sectionHeaderHeight = 46.0f;
}

#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{ // Change This to Non Zero Number
return 100;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{ // Change This to Non Zero Number
return 1;
}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}


// NSDate *object = _objects[indexPath.row];
cell.textLabel.text = @"";
return cell;
}

static NSString *SectionHeaderIdentifier = @"SectionHeaderIdentifier";

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
SectionHeaderView *headerView = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:SectionHeaderIdentifier];
if (headerView == nil) {
headerView = [[SectionHeaderView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(tableView.frame), tableView.sectionHeaderHeight)];
}
[headerView setTitle:@"Title" subTitle:@"Subtitle" image:[UIImage imageNamed:@"search-icon-timeline-translucent@2x.png"]];
return headerView;
}

UIColor+LightAndDark.h


//
// UIColor+LightAndDark.h
// Lua
//
// Created by Kurry Tran on 12/24/12.
// Copyright (c) 2012 Lua Technologies. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UIColor (LightAndDark)
- (UIColor *)lighterColor:(CGFloat)percent;
- (UIColor *)darkerColor:(CGFloat)percent;
@end

UIColor+LightAndDark.m


//
// UIColor+LightAndDark.m
// Lua
//
// Created by Kurry Tran on 12/24/12.
// Copyright (c) 2012 Lua Technologies. All rights reserved.
//

#import "UIColor+LightAndDark.h"

@implementation UIColor (LightAndDark)
//http://stackoverflow.com/questions/11598043/get-slightly-lighter-and-darker-color-from-uicolor

- (UIColor *)lighterColor:(CGFloat)percent
{
float h, s, b, a;
if ([self getHue:&h saturation:&s brightness:&b alpha:&a])
return [UIColor colorWithHue:h
saturation:s
brightness:MIN(b * (1+percent), 1.0)
alpha:a];
return nil;
}

- (UIColor *)darkerColor:(CGFloat)percent
{
float h, s, b, a;
if ([self getHue:&h saturation:&s brightness:&b alpha:&a])
return [UIColor colorWithHue:h
saturation:s
brightness:b * (1-percent)
alpha:a];
return nil;
}
@end

SectionHeaderView.h


//
// SectionHeaderView.h
// FastScrolling-UITableViews
//
// Created by Kurry L Tran on 6/26/13.
// Copyright (c) 2013 Kurry L Tran. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface SectionHeaderView : UIView
{
NSString *_title;
NSString *_subTitle;
UIImage *_image;
}
- (void)setTitle:(NSString *)title subTitle:(NSString *)subTitle;
- (void)setTitle:(NSString *)title subTitle:(NSString *)subTitle image:(UIImage *)image;

SectionHeaderView.m


//
// SectionHeaderView.m
// FastScrolling-UITableViews
//
// Created by Kurry L Tran on 6/26/13.
// Copyright (c) 2013 Kurry L Tran. All rights reserved.
//

#import "SectionHeaderView.h"
#import "UIColor+LightAndDark.h"
#import <QuartzCore/QuartzCore.h>

@implementation SectionHeaderView

- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}

- (void)setTitle:(NSString *)title subTitle:(NSString *)subTitle
{
_title = title;
_subTitle = subTitle;
[self setNeedsDisplay];
}

- (void)setTitle:(NSString *)title subTitle:(NSString *)subTitle image:(UIImage *)image
{
_title = title;
_subTitle = subTitle;
_image = image;
[self setNeedsDisplay];

}


// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
// This Creates a White A Black to White Gradient Layer to Blend With the Solid Color
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
[gradientLayer setFrame:rect];
NSArray *colors = [NSArray arrayWithObjects:
(id)[[UIColor whiteColor] CGColor],
(id)[[[UIColor blackColor] colorWithAlphaComponent:1.0f] CGColor],
nil];
gradientLayer.colors = colors;
gradientLayer.opacity = 0.09;
[self.layer addSublayer:gradientLayer];

// This Generates Three Random Numbers between [0,1] to seed random color
float r = (float)rand()/(float)RAND_MAX;
float g = (float)rand()/(float)RAND_MAX;
float b = (float)rand()/(float)RAND_MAX;

UIColor *baseColor = [UIColor colorWithRed:r green:g blue:b alpha:1.0f];
UIColor *topBorderColor = [baseColor lighterColor:0.20];
UIColor *bottomBorderColor = [baseColor darkerColor:0.20];

// This Draws The Background Color
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, baseColor.CGColor);
CGContextFillRect(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height));
CGContextSaveGState(context);

// This Draws The Top Border Color
CGContextMoveToPoint(context, CGRectGetMinX(rect), 0);
CGContextAddLineToPoint(context, CGRectGetMaxX(rect), 0);
CGContextSetStrokeColorWithColor(context, topBorderColor.CGColor);
CGContextSetLineWidth(context, 1);
CGContextStrokePath(context);
CGContextRestoreGState(context);

CGContextSaveGState(context);

// This Draws The Bottom Border Color
CGContextMoveToPoint(context, CGRectGetMinX(rect), CGRectGetMaxY(rect));
CGContextAddLineToPoint(context, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
CGContextSetStrokeColorWithColor(context, bottomBorderColor.CGColor);
CGContextSetLineWidth(context, 1);
CGContextStrokePath(context);
CGContextRestoreGState(context);

CGRect titleLabelFrame = self.bounds;
titleLabelFrame.origin.x = 69.0;
titleLabelFrame.origin.y = 8.0;

static UIColor *titleColor;
titleColor = [UIColor whiteColor];
static UIColor *subTitleColor;
subTitleColor = [UIColor whiteColor];
[titleColor set];
CGContextSaveGState(context);
context = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(context, kCGBlendModeNormal);

// Draws Title
[_title drawAtPoint:CGPointMake(titleLabelFrame.origin.x, titleLabelFrame.origin.y)
forWidth:200
withFont:[UIFont boldSystemFontOfSize:17]
fontSize:17
lineBreakMode:NSLineBreakByTruncatingTail
baselineAdjustment:UIBaselineAdjustmentAlignCenters];

// Draws SubTitle
[subTitleColor set];
[_subTitle drawAtPoint:CGPointMake(69, 25)
forWidth:200
withFont:[UIFont systemFontOfSize:13.0]
fontSize:13
lineBreakMode:NSLineBreakByTruncatingTail
baselineAdjustment:UIBaselineAdjustmentAlignCenters];

[_image drawInRect:CGRectMake(10.0, 10.0, 30.0, 28.0)];
}


@end


5. To custom draw fast tableview cells create a subclass of ABTableViewCell: https://github.com/enormego/ABTableViewCell, which draws the cell with core graphics. You should end up with something like this:
















ABTableViewCell.h


// Copyright (c) 2008 Loren Brichter
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// ABTableViewCell.h
//
// Created by Loren Brichter
// Copyright 2008 Loren Brichter. All rights reserved.
//

#import <UIKit/UIKit.h>

// to use: subclass ABTableViewCell and implement -drawContentView:
@interface ABTableViewCell : UITableViewCell
{
UIView *contentView;
}

- (void)drawContentView:(CGRect)r; // subclasses should implement

@end


ABTableViewCell.m


// Copyright (c) 2008 Loren Brichter
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// ABTableViewCell.m
//
// Created by Loren Brichter
// Copyright 2008 Loren Brichter. All rights reserved.
//

#import "ABTableViewCell.h"
@interface ABTableViewCellView : UIView
@end

@implementation ABTableViewCellView

- (void)drawRect:(CGRect)r
{
[(ABTableViewCell *)[self superview] drawContentView:r];
}

@end



@implementation ABTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
contentView = [[ABTableViewCellView alloc]initWithFrame:CGRectZero];
contentView.opaque = YES;
[self addSubview:contentView];
}
return self;
}


- (void)setFrame:(CGRect)f
{
[super setFrame:f];
CGRect b = [self bounds];
//b.size.height -= 1; // leave room for the seperator line
[contentView setFrame:b];
}

- (void)setNeedsDisplay
{
[super setNeedsDisplay];
[contentView setNeedsDisplay];
}

- (void)drawContentView:(CGRect)r
{
// subclasses should implement this
}

@end


KTTableViewCell.h


//
// KTTableViewCell.h
// FastScrolling-UITableViews
//
// Created by Kurry Tran on 6/27/13.
// Copyright (c) 2013 Kurry L Tran. All rights reserved.
//

#import "ABTableViewCell.h"

@interface KTTableViewCell : ABTableViewCell
{
NSString *_title;
NSString *_subTitle;
UIImage *_image;
}
- (void)setTitle:(NSString *)title subTitle:(NSString *)subtitle image:(UIImage *)image;
@end




//
// KTTableViewCell.m
// FastScrolling-UITableViews
//
// Created by Kurry Tran on 6/27/13.
// Copyright (c) 2013 Kurry L Tran. All rights reserved.
//

#import "KTTableViewCell.h"

@implementation KTTableViewCell

- (void)setTitle:(NSString *)title subTitle:(NSString *)subtitle image:(UIImage *)image
{
_title = title;
_subTitle = subtitle;
_image = image;
[self setNeedsDisplay];
}

-(void) drawContentView:(CGRect)r
{
static UIColor *textColor;
textColor = [UIColor blackColor];
static UIColor *borderColor;
borderColor = [UIColor colorWithRed:200.0f/255.0f green:200.0f/255.0f blue:200.0f/255.0f alpha:1.0f];
static UIColor *selectedColor;
selectedColor = [UIColor colorWithRed:212.0f/255.0f green:212.0f/255.0f blue:212.0f/255.0f alpha:1.0f];

CGContextRef context = UIGraphicsGetCurrentContext();

if(self.highlighted || self.selected)
{
r.origin.y -= 1.0f;
r.size.height += 1.0f;
CGContextSetFillColorWithColor(context, selectedColor.CGColor);
CGContextFillRect(context, CGRectMake(0, 0, CGRectGetWidth(r), CGRectGetMaxY(r)));
CGContextSaveGState(context);
}
else
{
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, r.size.width, r.size.height));
CGContextSaveGState(context);

CGContextMoveToPoint(context, CGRectGetMinX(r), 0);
CGContextAddLineToPoint(context, CGRectGetMaxX(r), 0);
CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
CGContextSetLineWidth(context, 1);
CGContextStrokePath(context);
CGContextRestoreGState(context);

CGContextSaveGState(context);

CGContextMoveToPoint(context, CGRectGetMinX(r), CGRectGetMaxY(r));
CGContextAddLineToPoint(context, CGRectGetMaxX(r), CGRectGetMaxY(r));
CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
CGContextSetLineWidth(context, 1);
CGContextStrokePath(context);
CGContextRestoreGState(context);
[_image drawInRect:CGRectMake(5, 5, 45, 45)];
}

[textColor set];
[_title drawAtPoint:CGPointMake(65.0f, 7.0f)
forWidth:200
withFont:[UIFont boldSystemFontOfSize:17]
fontSize:17
lineBreakMode:NSLineBreakByTruncatingTail
baselineAdjustment:UIBaselineAdjustmentAlignCenters];

[_subTitle drawAtPoint:CGPointMake(65.0f, 28.0f)
forWidth:200
withFont:[UIFont systemFontOfSize:16.0f]
fontSize:16
lineBreakMode:NSLineBreakByTruncatingTail
baselineAdjustment:UIBaselineAdjustmentAlignCenters];

}
@end


// Add This At The Top
#import "KTTableViewCell.h"

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
[self.navigationController.navigationBar configureFlatNavigationBarWithColor:[UIColor colorWithRed:44.0f/255.0f green:71.0f/255.0f blue:98.0f/255.0f alpha:1.0f]];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
[UIBarButtonItem configureFlatButtonsWithColor:[UIColor colorWithRed:52.0f/255.0f green:152.0f/255.0f blue:219.0f/255.0f alpha:1.0f]
highlightedColor:[UIColor colorWithRed:41.0f/255 green:128.0f/255 blue:185.0f/255 alpha:1.0f]
cornerRadius:3];

self.tableView.sectionHeaderHeight = 46.0f;
// Add This Line
self.tableView.rowHeight = 56.0f;
}

// Change this to your new subclass
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";

KTTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[KTTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
[cell setTitle:@"Title" subTitle:@"Subtitle" image:[UIImage imageNamed:@"placeholder@2x.png"]];

// NSDate *object = _objects[indexPath.row];
cell.textLabel.text = @"";
return cell;
}


Full Source Code Here: http://db.tt/7E2EBbln




Tuesday, June 25, 2013

Compiling Objective-C Command Line in OS X Lion and OS X Mountain Lion

Method two worked on my MacBook Air running OS X Lion, and Method One worked on my MacBook Pro running OS X Mountain Lion.

Makefile


default: method_two

method_one:
export PATH=$PATH:/System/Library/Frameworks
clang -fobjc-arc main.m –framework Foundation -o program
./program

method_two:
clang -fobjc-gc /System/Library/Frameworks/Foundation.framework/Foundation main.m -o program
./program

clean:
rm -f program

main.m


// Remove the \ character -- Google Blogger is Having Problems
#import <\Foundation/Foundation.h\>

int main(int argc, char *argv[])
{
@autoreleasepool {
NSLog(@"Hello World!\n");
}
return 0;
}

Monday, June 24, 2013

teehan + lax iOS 7 GUI PSD

Hidden Gems in Cocoa Touch / XCode

1. Open Quickly (Command-Shift-O)
2. View Related Files From Toolbar
3. Use breakpoint actions such as log message or play a sound to avoid recompiling.
4. Use debugDescription.
5. Use Objective-C Subscripting.
NSMutableArray *indexedValues = [NSMutableArray array];

indexedValues[0] = @"One";

NSLog(@"value: %@", indexedValues[0]);

NSMutableDictionary *keyedValues = [NSMutableDictionary dictionary];

keyedValues[@"color"] = [UIColor blueColor];

NSLog(@"value: %@", keyedValues[@"color"]);

6. Private declarations not necessary since Xcode 4.3
7. Synthesize not necessary for @property since Xcode 4.4
8. Reverse arrays quickly inline

 NSArray *numbers = @[ @1, @2, @3 ];

 NSArray *reversed = numbers.reverseObjectEnumerator.allObjects;

9.  Guarantee a mutable object

 NSArray *unknown = self.values; // may be nil

 NSMutableArray *newArray = [NSMutableArray arrayWithArray:unknown];

10. Declare and enumerate different collection types

 id collection = values;

 for (id object in collection) {

... }

11. Remove duplicate values in array without NSSet

[array valueForKeyPath:@"@distinctUnionOfObjects.self"]

12. CAGradientLayer

CAGradientLayer *gradient = [CAGradientLayer layer];

gradient.frame = CGRectMake(150, 250, 500, 500);

UIColor *c1 = [UIColor colorWithRed:0.09 green:0.70 blue:0.98 alpha:1.0];

UIColor *c2 = [UIColor colorWithRed:0.07 green:0.41 blue:0.95 alpha:1.0];

UIColor *c3 = [UIColor colorWithRed:0.81 green:0.46 blue:0.93 alpha:1.0];

gradient.colors = @[(id)c2.CGColor, (id)c3.CGColor, (id)c3.CGColor];

CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"colors"];

anim.toValue = @[(id)c1.CGColor, (id)c2.CGColor, (id)c2.CGColor];

anim.duration = 4.0;

anim.autoreverses = YES;

anim.repeatCount = 1e100;

[gradient addAnimation:anim forKey:@"colors"];

[self.view.layer addSublayer:gradient];



13. Core Data Private Queue

NSManagedObjectContext *bgContext;

bgContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:

                                    NSPrivateQueueConcurrencyType];

[context performBlock:^{

    // (add, remove, change objects.)

    saveCompleted = [context save:& saveError];

}];



14. Core Data
Really fast fetches
• Only specific properties

 NSFetchRequest

 fetch.propertiesToFetch = @[@"name", @"phone"];

• Only the raw values

fetch.resultType = NSDictionaryResultType
• Only the object id

 fetch.resultType = NSManagedObjectIDResultType

• Only the count

 fetch.resultType = NSCountResultType



WWDC 2013 Core Data Performance - Optimization and Debugging

These are the things that I thought were most useful to me:

1. Don't fetch more than you need. Only 10 or so rows are visible so set a batch size on your NSFetchRequests.

Example:


NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Contact"];

request.fetchBatchSize = 20;


2. Optimize your data model. Put binary data in separate entities and use external storage. Duplication isn't always a bad thing, if it speeds up your data query.

3. Prefetch relationships if you know you need them.

4. Consider returning dictionaries instead of managed objects.

Example:


NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Quake"];

[request setResultType:NSDictionaryResultType];

[request setPropertiesToFetch:@[@"magnitude"]];


5. Use SQLite to perform your calculations.

Example:


NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Quake"];
NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
 ed.name = @"minimum";
 ed.expression = [NSExpression expressionForFunction:@"min:"
            arguments:@[ [NSExpression expressionForKeyPath:@"magnitude"]]];
[request setPropertiesToFetch:@[ ed ]];


6. Use SQLite to group your results automatically.

Example:


NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
 ed.name = @"count";
 ed.expression = [NSExpression expressionForFunction:@"count:"
arguments:@[ [NSExpression expressionForKeyPath:@"magnitude"]]];
 [request setPropertiesToFetch:@[ @"magnitude", ed ]];
 [request setPropertiesToGroupBy:@[ @"magnitude" ]];
7. Use SQL Logging.

Pass argument flags = {1,2,3} on launch:


-com.apple.CoreData.SQLDebug 1


8. Optimize predicates. Text comparisons are expensive. Put numeric comparisons first.


NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Contact"];
Bad:


request.predicate = [NSPredicate predicateWithFormat:@"firstName == %@ AND age > %i", @"John", 40];
Good:

request.predicate = [NSPredicate predicateWithFormat:@"age > %i && firstName == %@", 40, @"John"];
9. Predicate Costs (Low to High):


Beginswith/Endswith < Equality < Contains < Matches




iOS 5 NSFetchedResultsController Sorting Issues With RestKit

We were having issues with NSManagedObjects not being sorted correctly after modifying managed objects locally and saving, but we didn't know why so I had to do some research. I found this post: http://wbyoung.tumblr.com/post/27851725562/core-data-growing-pains which was helpful, and the code snippet below was what fixed it. The issue in iOS 5 is that the NSFetchedResultsController deadlocks when there are changes in a child context. So with RestKit on that particular view controller we just had to switch from using the mainQueueManagedObjectContext to mainQueueManagedObjectContext.parentContext for iOS 5 and it resolved all of our issues.
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)


NSManagedObjectContext *newManagedObjectContext;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
newManagedObjectContext = [RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext;
} else{
newManagedObjectContext = [RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext.parentContext;
}
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
NSFetchedResultsController *newFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:newManagedObjectContext sectionNameKeyPath:nil cacheName:nil];