Showing posts with label uitableview. Show all posts
Showing posts with label uitableview. Show all posts

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




Sunday, December 23, 2012

Tutorial: Adding a Drop Shadow To Last Row of UITableView


//
//  UITableViewCell+DropShadow.h
//  TableViewCellDropShadow
//
//  Created by Kurry L Tran on 12/23/12.
//  Copyright (c) 2012 Kurry L Tran. All rights reserved.
//
 
#import <UIKit/UIKit.h>
 
@interface UITableViewCell (DropShadow)
-(void)addDropShadow;
-(void)removeDropShadow;
@end
//
//  UITableViewCell+DropShadow.m
//  TableViewCellDropShadow
//
//  Created by Kurry L Tran on 12/23/12.
//  Copyright (c) 2012 Kurry L Tran. All rights reserved.
//
 
#import "UITableViewCell+DropShadow.h"
#import <QuartzCore/QuartzCore.h>
 
@implementation UITableViewCell (DropShadow)
 
-(void)addDropShadow
{
  self.layer.shadowOffset = CGSizeMake(0, 10);
  self.layer.shadowColor = [[UIColor blackColor] CGColor];
  self.layer.shadowRadius = 3;
  self.layer.shadowOpacity = .75;
  CGRect shadowFrame = self.layer.bounds;
  CGPathRef shadowPath = [UIBezierPath bezierPathWithRect:shadowFrame].CGPath;
  self.layer.shadowPath = shadowPath;
}
 
-(void)removeDropShadow
{
  self.layer.shadowOpacity = 0;
}
@end

Example Usage: 

// 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 = [object description];
  NSInteger numberOfSections = [self numberOfSectionsInTableView:tableView];
  NSInteger numberOfRows = [self tableView:tableView numberOfRowsInSection:(numberOfSections-1)];

  if (indexPath.section == (numberOfSections - 1) && indexPath.row == (numberOfRows-1))
    [cell addDropShadow];
  else
   [cell removeDropShadow];

  return cell;
}

- (void)viewWillAppear:(BOOL)animated

{
  NSInteger numberOfSections = [self numberOfSectionsInTableView:self.tableView];
  NSInteger numberOfRows = [self tableView:self.tableView numberOfRowsInSection:(numberOfSections-1)];
  NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(numberOfRows -1) inSection:(numberOfSections-1)];
  UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
  [cell addDropShadow];
}










Sample Project : http://bit.ly/W10XCU

Tuesday, October 25, 2011

iOS 5 Storyboard + UITableView Tutorial II (Improved)

Since I'm learning as well and have been reading other people's tutorials that I thought were not very good (because they either had too much clutter or did not stress the important points) I thought I would make another tutorial on the key ideas of UITableViews that I missed out on before.

Download Project: here.
1. Create a new Tabbed Application called StoryboardUITableView.



2. In the MainStoryboard file look at the attribute inspector and you will see the checkbox for Is Initial View Controller.

Common Error: Not having set an initial view controller. The simulator will popup and then just be in a deadlock and in the output log, it will say something about how your application doesn't not have a root view controller.



3. Embed a navigation controller in the first view by going to Editor > Embed In > Navigation Controller.

Note: If you delete the connection, and then control+drag from the Tab Bar Controller you will see the following in the image below. You can set the initial scene to be the first view controller by control + dragging as described before and then selecting Relationship - viewControllers. This creates a connection between the Tab Bar Controller and and Navigation Controller.




4. Suppose I create a tableview by drag and dropping it a UITableViewController, I can customize the cells in the right panel.

Key Points:

Style: Change Style of UITableViewCell
Identifier: Cell Identifier to Distinguish Prototype Cells
Accessory: The Creates The Right Facing Arrow
Size: Resize the cell by dragging it up or down.

Note: Ignore the left half of the screenshot since I made an error the first time.


5. Create a new UITableViewController called AlbumViewController and select the table view controller and change its class to AlbumViewController .




6. Suppose I created a datasource in the AppDelegate.m file and I follow the usual steps to populate the tableview. The one interesting change is here: tableview cells are now automatically allocated so the code is much simpler than before. In the AlbumViewController.m file implement the usual methods.

 (Note: Copy and paste them from the source file in the download.)


7. Change this method and you can then slide to delete a tableview cell.




8. Run and project and it will turn out like this.


Download Project: here.

http://kurrytran.blogspot.com/2013/06/fast-scrolling-customized-uitableviews.html

Wednesday, October 19, 2011

iOS 5 Storyboard and UITableViews Tutorial

In this tutorial we will:

1. Create a UITableView from scratch.
2. Learn about navigation controllers.
3. Pushing view controllers with storyboard.

Link to Download Entire Project: here

1. Create a new single view based application called "StoryboardTutorial".


2. Select the main storyboard file and then select Editor > Embed In > Navigation Controller.
Note: A navigation controller is a way to manage views. Typically you push or pop a view onto a navigation controller. You can think of this a stack.


3. Next drag and drop a UITableView from the right sidebar and place it onto the view controller.


4. Go into the ViewController.h file and add after it says UIViewController. This tells the compiler that your class will conform to the UITableViewDelegate/UITableViewDataSource protocols. A protocol is just a formal way of saying that you will implement all methods that are required so you have a functioning tableview. 




Note: The biggest benefit of declaring the ViewController.h object as such is that now Xcode will autogenerate code and most of your necessary methods you would like to implement in the tableview class. Also note, if the method is not auto-generating you're probably doing something wrong. That's the main way I know I'm doing something wrong, or the program crashes.



5. Go back to the main storyboard file and "Control + Drag" from the tableview to the view controller and select "data source" and "delegate". This is how you tell the tableview where it is going to get it's data from, and if an event (a touch event for example) happens, who should it notify.


6. Now click on a tableview cell and click on the right side bar and go to the fourth panel from the left. In the field Identifier type "Cell". This is label so that you can reuse tableview cells which saves memory.


7. In the ViewController.m file implement these protocol methods.
Note: Philosophically it would make sense that you would needs to implement these methods to have create an actually tableview. You need to know 1) how many rows should you have and 2) what should you label these rows and 3) what to do when a user clicks on it.

Debugging Tip: Manually label the cells at first to make sure you made your connections correctly. I once spent three hours of my life trying to figure out why my tableview was empty. Save yourself the misery.



After you implement those methods it should look like this. I changed the table to states because I wanted to later show how to alphabetize a list of states.


8. Go back to the main storyboard file and drag and drop a View Controller object, and in the right panel type "detail" and the Identifier. This will be the detail view controller to push another view onto the view controller.


9. Go to File > New File > UIViewController Subclass and then create a UIViewController subclass called "DetailViewController" and unselect use XIB for interface.



10. Go back to the main storyboard file and in the right panel change the class of your newest view controller to be the "DetailViewController" that you just created.


11. Now go back to the ViewController.h file and then add "#import DetailViewController.h" at the top of your file. Then edit the two methods like below.

Note: What's new in iOS 5 is the storyboard and transitioning from one view to the other. Instead of having lots of Xib files, you replace them with views all placed into one storyboard file. This makes life much easier for the developer. So seen below is how you create a view controller object in storyboard.
"[self.storyboard instantiateViewControllerWithIdentifier:@"detail"" tells the computer to create the view that I called "detail" in the storyboard file, and then the "[self.navigationController pushViewController:detail animated:YES]" then pushes the view onto the view stack, so the visible view is then the current view.

Note: The tableview height method is self-explanatory. For some reason I had a hard time finding that method when I first started so I thought I would include it here for people to play with.


If everything is working correctly you should see these two screens if you select onto a tableview cell.

Note: In keeping with the states them I added two label to the detail view to work with later on.




12. Now if you can add a background image to the tableview cell by editing the cellForRowAtIndexPath method. Here I have a 320x65 pixel image called "LightGrey.png" which I'll include in the project at the end of the tutorial. Adding a background image really adds vibrance to your tableview.





13. Create a datasource by editing your ViewController.h and ViewController.m files so they match the ones below.
















Then make these changes in the ViewController.m file:



Changes:
1) @synthesize states and datasource.
2) In the viewDidLoad method call the function setupArray;
3) Create the datasource in setupArray;
4) Change the text label so that it reads from the datasource.

14. Edit the DetailViewController.h file so it matches the one below.



Then @synthesize them at the top of the DetailViewController.m file.


Then edit the viewDidLoad method.


15. Go back to the main storyboard file and select the detail view controller and "Control + Drag" from the DetailViewController to each of the UILabels and connect them to the appropriate outlet. 








16. Then go back to the ViewController.m file and edit the didSelectRowAtIndexPathMethod so it matches the following. This is just passing data from one view to the other.

So now when you select the state, the detail view will show the state's name and the state capital.