2012年3月18日 星期日

Filter4Cam 學習之 Scroll Horizontally Tables: Part 2

since: 2012/03/18
update: 2012/03/26

reference:
How To Make An Interface With Horizontal Tables Like The Pulse News App: Part 2

A. 說明
      1. 接續之前的專案: I touchs: Filter4Cam 學習之 Scroll Horizontally Tables: Part 1   
      2. 更新成採用 iOS 5 的 ARC 機制.
      3. 詳細的說明, 還是要看原作者的文章.

---------------------------------------------------------------------------------------

B. 建立 UITableViewCell 子類別
     1. 說明: a. 使用 Apple 預設的 cell 樣式, 可以有子標題或圖片, 但是無法嵌入一個
                        Table View.
                   b. 之後將會建立一個 UITableView, 旋轉後加入成為 cell 的 subview.
                        (需要覆寫 initWithFrame 方法)

     2. Xcode > File > New > File...
         > iOS > Cocoa Touch > Objective-C class > Next
        Class: HorizontalTableCell
        Subclass of: UITableViewCell
        > Next > Create

     3. 開啓 HorizontalTableCell.h 檔案, 修改如下:
#import <UIKit/UIKit.h>

//@interface HorizontalTableCell : UITableViewCell
//@update
@interface HorizontalTableCell : UITableViewCell <UITableViewDelegate, UITableViewDataSource>
{
    //@add
    UITableView *horizontalTableView;
    NSArray *articles;
}

//@add
@property (nonatomic, strong) UITableView *horizontalTableView;
@property (nonatomic, strong) NSArray *articles;

@end

     4. 開啓 HorizontalTableCell.m 檔案, 修改如下:
#import "HorizontalTableCell.h"

@implementation HorizontalTableCell

//@add
@synthesize horizontalTableView = _horizontalTableView;
@synthesize articles = _articles;

#pragma mark - Table View Data Source
//@add
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{   
    return [self.articles count];
}

//@add
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"ArticleCell";
   
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
   
    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    }
   
    cell.textLabel.text = @"The title of the cell in the table within the table :O";
   
    return cell;
}

#pragma mark - Memory Management

//@add
- (void)dealloc
{
    self.horizontalTableView = nil;
    self.articles = nil;
}

/*
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}
*/

/*
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}
*/

@end

---------------------------------------------------------------------------------------

C. 建立 Utility File
   1. Xcode > File > New > File...
         > iOS > C and C++ > Header File > Next

        Save As: ControlVariables
        Targets: HorizontalTables
        > Create

   2. 開啓 ControlVariables.h 檔案, 修改如下:
// Width (or length before rotation) of the table view embedded
// within another table view's row

#define kTableLength                                320

// Width of the cells of the embedded table view (after rotation, which means
// it controls the rowHeight property)

#define kCellWidth                                  106

// Height of the cells of the embedded table view (after rotation, which would
// be the table's width)

#define kCellHeight                                 106

// Padding for the Cell containing the article image and title
#define kArticleCellVerticalInnerPadding            3
#define kArticleCellHorizontalInnerPadding          3

// Padding for the title label in an article's cell
#define kArticleTitleLabelPadding                   4

// Vertical padding for the embedded table view within the row
#define kRowVerticalPadding                         0

// Horizontal padding for the embedded table view within the row
#define kRowHorizontalPadding                       0

// The background color of the vertical table view
#define kVerticalTableBackgroundColor               [UIColor colorWithRed:0.58823529 green:0.58823529 blue:0.58823529 alpha:1.0]

// Background color for the horizontal table view (the one embedded inside
// the rows of our vertical table)

#define kHorizontalTableBackgroundColor             [UIColor colorWithRed:0.6745098 green:0.6745098 blue:0.6745098 alpha:1.0]

// The background color on the horizontal table view for when we
// select a particular cell

#define kHorizontalTableSelectedBackgroundColor     [UIColor colorWithRed:0.0 green:0.59607843 blue:0.37254902 alpha:1.0]

   3. 開啓 HorizontalTableCell.h 檔案, 修改如下:
#import <UIKit/UIKit.h>
//@add
#import "ControlVariables.h"
....

---------------------------------------------------------------------------------------

D. 建立 HorizontalTableCell 子類別
     1. Xcode > File > New > File...
         > iOS > Cocoa Touch > Objective-C class > Next
        Class: HorizontalTableCell_iPhone
        Subclass of: HorizontalTableCell
        > Next > Create

   2. 開啓 HorizontalTableCell_iPhone.m 檔案, 修改如下:
#import "HorizontalTableCell_iPhone.h"

@implementation HorizontalTableCell_iPhone

//@add
- (NSString *) reuseIdentifier
{
    return @"HorizontalCell";
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        //@add
        self.horizontalTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, kCellHeight, kTableLength)];

        self.horizontalTableView.showsVerticalScrollIndicator = NO;
        self.horizontalTableView.showsHorizontalScrollIndicator = NO;

        self.horizontalTableView.transform = CGAffineTransformMakeRotation(-M_PI * 0.5);

        [self.horizontalTableView setFrame:CGRectMake(kRowHorizontalPadding * 0.5, kRowVerticalPadding * 0.5, kTableLength - kRowHorizontalPadding, kCellHeight)];
       
        self.horizontalTableView.rowHeight = kCellWidth;
        self.horizontalTableView.backgroundColor = kHorizontalTableBackgroundColor;
       
        self.horizontalTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
        self.horizontalTableView.separatorColor = [UIColor clearColor];
       
        self.horizontalTableView.dataSource = self;
        self.horizontalTableView.delegate = self;
        [self addSubview:self.horizontalTableView];
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

@end

---------------------------------------------------------------------------------------

E. 建立 Article Cells
     1. 說明:
        目前已經有垂直的 table view, 並且建立了包含有水平 table 的客製化
         UITableViewCell, 但是為了讓介面看起來更酷, 所以我們需要另一個客製化
         的 cell, 來將水平的 table 放入. 

     2. 建立 UITableViewCell 子類別
         Xcode > File > New > File...
         > iOS > Cocoa Touch > Objective-C class > Next
        Class: ArticleCell
        Subclass of: UITableViewCell
        > Next > Create

     3. 開啓 ArticleCell.h 檔案, 修改如下:
#import <UIKit/UIKit.h>

@interface ArticleCell : UITableViewCell
{
    //@add
    // hold the thumbnail picture for our article
    UIImageView *thumbnail;
   
    // the title of our article
    UILabel *titleLabel;
}

//@add
@property (nonatomic, strong) UIImageView *thumbnail;
@property (nonatomic, strong) UILabel *titleLabel;

@end

     4. 開啓 ArticleCell.m 檔案, 修改如下:
#import "ArticleCell.h"

@implementation ArticleCell

//@add
@synthesize thumbnail = _thumbnail;
@synthesize titleLabel = _titleLabel;

//@add
- (NSString *)reuseIdentifier
{
    return @"ArticleCell";
}

//@add
- (void)dealloc
{
    self.thumbnail = nil;
    self.titleLabel = nil;
}

/*
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}
*/

/*
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}
*/

@end

---------------------------------------------------------------------------------------

F. 建立 ArticleCell 子類別
     1. Xcode > File > New > File...
         > iOS > Cocoa Touch > Objective-C class > Next
        Class: ArticleCell_iPhone
        Subclass of: ArticleCell
        > Next > Create

     2. 開啓 ArticleCell_iPhone.m 檔案, 修改如下:
#import "ArticleCell_iPhone.h"
//@add
#import "ControlVariables.h"

@implementation ArticleCell_iPhone

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        //@add
        self.thumbnail = [[UIImageView alloc] initWithFrame:CGRectMake(kArticleCellHorizontalInnerPadding, kArticleCellVerticalInnerPadding, kCellWidth - kArticleCellHorizontalInnerPadding * 2, kCellHeight - kArticleCellVerticalInnerPadding * 2)];
       
        self.thumbnail.opaque = YES;
       
        [self.contentView addSubview:self.thumbnail];
       
        self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, self.thumbnail.frame.size.height * 0.632, self.thumbnail.frame.size.width, self.thumbnail.frame.size.height * 0.37)];
       
        self.titleLabel.opaque = YES;

        self.titleLabel.backgroundColor = [UIColor colorWithRed:0 green:0.4745098 blue:0.29019808 alpha:0.9];

        self.titleLabel.textColor = [UIColor whiteColor];
        self.titleLabel.font = [UIFont boldSystemFontOfSize:11];
        self.titleLabel.numberOfLines = 2;
        [self.thumbnail addSubview:self.titleLabel];
       
        self.backgroundColor = [UIColor colorWithRed:0 green:0.40784314 blue:0.21568627 alpha:1.0];

        self.selectedBackgroundView = [[UIView alloc] initWithFrame:self.thumbnail.frame];
        self.selectedBackgroundView.backgroundColor = kHorizontalTableSelectedBackgroundColor;
       
        self.transform = CGAffineTransformMakeRotation(M_PI * 0.5);
       
    }
    return self;
}


/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

@end

---------------------------------------------------------------------------------------

G. 最後的步驟
     1. 開啓 HorizontalTableCell_iPhone.m 檔案, 修改如下:
#import "HorizontalTableCell_iPhone.h"
//@add
#import "ArticleCell_iPhone.h"

@implementation HorizontalTableCell_iPhone

//@add
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ArticleCell";
   
    ArticleCell_iPhone *cell = (ArticleCell_iPhone *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
   
    if (cell == nil)
    {
        cell = [[ArticleCell_iPhone alloc] initWithFrame:CGRectMake(0, 0, kCellWidth, kCellHeight)];
    }
   
    NSDictionary *currentArticle = [self.articles objectAtIndex:indexPath.row];
   
    cell.thumbnail.image = [UIImage imageNamed:[currentArticle objectForKey:@"ImageName"]];
    cell.titleLabel.text = [currentArticle objectForKey:@"Title"];
   
    return cell;
}
....

     2. 開啓 ArticleListViewController.m 檔案, 修改如下:
....
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{ 
    //@update
    return 1;
}
....

     3. 開啓 ArticleListViewController_iPhone.m 檔案,  修改如下:
//@add
#define kHeadlineSectionHeight  26
#define kRegularSectionHeight   18

#import "ArticleListViewController_iPhone.h"
//@add
#import "HorizontalTableCell_iPhone.h"
#import "ControlVariables.h"
....
//@add
- (void)awakeFromNib
{
    [self.tableView setBackgroundColor:kVerticalTableBackgroundColor];

    self.tableView.rowHeight = kCellHeight + (kRowVerticalPadding * 0.5) + ((kRowVerticalPadding * 0.5) * 0.5);
}
....
//@add
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /*
....
    */
   
    static NSString *CellIdentifier = @"HorizontalCell";
   
    HorizontalTableCell_iPhone *cell = (HorizontalTableCell_iPhone *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
   
    if (cell == nil)
    {
        cell = [[HorizontalTableCell_iPhone alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, tableView.frame.size.height)];
    }
   
    NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES selector:@selector(localizedCompare:)];
   
    NSArray* sortedCategories = [self.articleDictionary.allKeys sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
   
    NSString *categoryName = [sortedCategories objectAtIndex:indexPath.section];
   
    NSArray *currentCategory = [self.articleDictionary objectForKey:categoryName];
   
    cell.articles = currentCategory;
   
    return cell;
}
....

     4. 編譯並執行:

---------------------------------------------------------------------------------------

H. 修正 Label 的錯誤
      1. 說明: 當點選一篇文章時, title label 背景色不見了.

      2. 建立一個 UILabel 子類別
          Xcode > File > New > File...
         > iOS > Cocoa Touch > Objective-C class > Next
        Class: ArticleTitleLabel
        Subclass of: UILabel
        > Next > Create

      3. 開啓 ArticleTitleLabel.h 檔案, 修改如下:
#import <UIKit/UIKit.h>

@interface ArticleTitleLabel : UILabel
{
}

//@add
- (void)setPersistentBackgroundColor:(UIColor*)color;

@end

      4. 開啓 ArticleTitleLabel.m 檔案, 修改如下:
#import "ArticleTitleLabel.h"
//@add
#import "ControlVariables.h"

@implementation ArticleTitleLabel

//@add
- (void)setPersistentBackgroundColor:(UIColor*)color
{
    super.backgroundColor = color;
}

//@add
- (void)setBackgroundColor:(UIColor *)color
{
    // do nothing - background color never changes
}

//@add
- (void)drawTextInRect:(CGRect)rect
{   
    CGFloat newWidth = rect.size.width - kArticleTitleLabelPadding;
    CGFloat newHeight = rect.size.height;
   
    CGRect newRect = CGRectMake(kArticleTitleLabelPadding * 0.5, 0, newWidth, newHeight);
   
    [super drawTextInRect:newRect];
}

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

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

@end

      5. 開啓 ArticleCell.h 檔案, 修改如下:
#import <UIKit/UIKit.h>

//@add
@class ArticleTitleLabel;

@interface ArticleCell : UITableViewCell
{
    //@add
    // hold the thumbnail picture for our article
    UIImageView *thumbnail;
   
    // the title of our article
    //UILabel *titleLabel;
    //@update
    ArticleTitleLabel *titleLabel;
}

//@add
@property (nonatomic, strong) UIImageView *thumbnail;
//@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) ArticleTitleLabel *titleLabel;

@end

      6. 開啓 ArticleCell_iPhone.m 檔案, 修改如下:
#import "ArticleCell_iPhone.h"
//@add
#import "ControlVariables.h"
#import "ArticleTitleLabel.h"
....
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
....       
         //self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, self.thumbnail.frame.size.height * 0.632, self.thumbnail.frame.size.width, self.thumbnail.frame.size.height * 0.37)];
        //@update
        self.titleLabel = [[ArticleTitleLabel alloc] initWithFrame:CGRectMake(0, self.thumbnail.frame.size.height * 0.632, self.thumbnail.frame.size.width, self.thumbnail.frame.size.height * 0.37)];
....
        //self.titleLabel.backgroundColor = [UIColor colorWithRed:0 green:0.4745098 blue:0.29019808 alpha:0.9];
        //@update
        [self.titleLabel setPersistentBackgroundColor:[UIColor colorWithRed:0 green:0.4745098 blue:0.29019808 alpha:0.9]];
       .... 
    }
    return self;
}
....

      7. 開啓 HorizontalTableCell_iPhone.m 檔案, 修改如下:
#import "HorizontalTableCell_iPhone.h"
//@add
#import "ArticleCell_iPhone.h"
#import "ArticleTitleLabel.h"
....

      8. 編譯並執行

---------------------------------------------------------------------------------------

I. 修正重覆出現列的情況
    1. 開啓 ArticleListViewController.h 檔案, 修改如下:
#import <UIKit/UIKit.h>

@interface ArticleListViewController : UITableViewController
{
    //@add
    NSDictionary *articleDictionary;
    NSMutableArray *reusableCells;
}
//@add
@property (nonatomic, strong) NSDictionary *articleDictionary;
@property (nonatomic, strong) NSMutableArray *reusableCells;

@end

    2. 開啓 ArticleListViewController.m 檔案, 修改如下:
....
//@add
@synthesize articleDictionary = _articleDictionary;
@synthesize reusableCells = _reusableCells;
....
- (void)viewDidUnload
{
    [super viewDidUnload];
    //@add
    self.articleDictionary = nil;
    self.reusableCells = nil;
}
....

    3. 開啓 ArticleListViewController_iPhone.m 檔案, 修改如下:
....
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    //@add
    if (!self.reusableCells)
    {      
        NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES selector:@selector(localizedCompare:)];
       
        NSArray* sortedCategories = [self.articleDictionary.allKeys sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
       
        NSString *categoryName;
        NSArray *currentCategory;
       
        self.reusableCells = [NSMutableArray array];
       
        for (int i = 0; i < [self.articleDictionary.allKeys count]; i++)
        {                       
            HorizontalTableCell_iPhone *cell = [[HorizontalTableCell_iPhone alloc] initWithFrame:CGRectMake(0, 0, 320, 416)];
           
            categoryName = [sortedCategories objectAtIndex:i];
            currentCategory = [self.articleDictionary objectForKey:categoryName];
            cell.articles = [NSArray arrayWithArray:currentCategory];
           
            [self.reusableCells addObject:cell];
        }
    }
}
....
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
/*
....
*/
    //@update
    HorizontalTableCell *cell = [self.reusableCells objectAtIndex:indexPath.section];
   
    return cell;
}
....
     4. 開啓 HorizontalTableCell.m 檔案, 修改如下: (update: 2012/03/26)
....
//@update: comment it
// Since we are no longer using the provided method for reusable cells,
// we don't need an identifier for them.

/*
- (NSString *)reuseIdentifier
{
    return @"HorizontalCell";
}
*/
....

    5. 編譯並執行:

---------------------------------------------------------------------------------------

J. 最後的效能調整: GCD (Grand Central Dispatch)
     開啓 HorizontalTableCell_iPhone.m 檔案, 修改如下:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ArticleCell";
   
    //ArticleCell_iPhone *cell = (ArticleCell_iPhone *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    //@update
    __block ArticleCell_iPhone *cell = (ArticleCell_iPhone *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
   
    if (cell == nil)
    {
        cell = [[ArticleCell_iPhone alloc] initWithFrame:CGRectMake(0, 0, kCellWidth, kCellHeight)];
    }
   
    //NSDictionary *currentArticle = [self.articles objectAtIndex:indexPath.row];
    //@update
    __block NSDictionary *currentArticle = [self.articles objectAtIndex:indexPath.row];
   
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   
    //cell.thumbnail.image = [UIImage imageNamed:[currentArticle objectForKey:@"ImageName"]];
    //@update
    dispatch_async(concurrentQueue, ^{       
        UIImage *image = nil;       
        image = [UIImage imageNamed:[currentArticle objectForKey:@"ImageName"]];
       
        dispatch_async(dispatch_get_main_queue(), ^{
            [cell.thumbnail setImage:image];
        });
    });
   
    cell.titleLabel.text = [currentArticle objectForKey:@"Title"];
   
    return cell;
}

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。