views:

229

answers:

4

Imagine you have a normal table view where each row is an item on a conveyor belt. You will put the items in each cell of the table view but when you scroll you also want the background image (the conveyor belt) to scroll as well. How can you do this?

+2  A: 

You should be able to accomplish this by setting the background color of the table view:

tableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background.png"]];
Shaggy Frog
Although this will work but this will great only when you have a plain image. Suppose you an image of a person then it will look very much distorted in the table view cells
Madhup
Is there another (reasonable) alternative, given the OP wants the background to scroll *with* the table view? I can't think of one myself.
Shaggy Frog
+1  A: 

I haven't tried this, so I'm not sure what the best approach would be, but one option would be to add your background image as a UIImageView to each of your cells so that every cell has a full-sized copy of your background image. Set clipsToBounds to NO on your cell, and give the bounds of the UIImageView a negative y value equal to the offset from your cell to the top of the table.

You may also want to consider using UIScrollView instead of UITableView.

UITableView is itself a UIScrollView, so you could try just adding your background image as a subview of your UITableView, but I'd be surprised if that worked. I'm guessing the UITableView implementation won't play nice with foreign subviews.

** EDIT **

While I still suspect that UIScrollView may be a more appropriate base class to use here, I decided to try the UIImageView trick I described above. It's fairly simple and doesn't consume excessive memory as long as all your UIImageViews share a single UIImage. Here's my sample code:

//  LadderCell.h
#import <UIKit/UIKit.h>

@interface LadderCell : UITableViewCell {
    UIImageView *backgroundImageView;
    UILabel *titleLabel;
}

@property (nonatomic, retain) UIImageView *backgroundImageView;
@property (nonatomic, retain) UILabel *titleLabel;

- (void)setIndexPath:(NSIndexPath *)indexPath;
- (id)initWithImage:(UIImage *)theImage;
+ (NSString *)reuseIdentifier;
+ (CGFloat)height;
@end
//  LadderCell.m
#import "LadderCell.h"

@implementation LadderCell
@synthesize backgroundImageView, titleLabel;

- (void)dealloc {
    self.backgroundImageView = nil;
    self.titleLabel = nil;
    [super dealloc];
}

- (id)initWithImage:(UIImage *)theImage {
    if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[LadderCell reuseIdentifier]]) {
        self.frame = CGRectMake(0.0, 0.0, 320.0, [LadderCell height]);
        self.clipsToBounds = YES;
        self.backgroundImageView = [[[UIImageView alloc] initWithImage:theImage] autorelease];
        backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
        self.titleLabel = [[[UILabel alloc] initWithFrame:self.bounds] autorelease];
        titleLabel.textAlignment = UITextAlignmentCenter;
        titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        titleLabel.backgroundColor = [UIColor colorWithWhite:1 alpha:0];
        [self addSubview:backgroundImageView];
        [self addSubview:titleLabel];
    }
    return self;
}

- (void)setIndexPath:(NSIndexPath *)indexPath {
    backgroundImageView.frame = CGRectMake(0.0, 
                                           -(CGFloat)indexPath.row * [LadderCell height] + 100.0,  
                                           backgroundImageView.frame.size.width,
                                           backgroundImageView.frame.size.height);
}

+ (NSString *)reuseIdentifier {
    return @"LadderCell";
}

+ (CGFloat)height {
    return 30;

}

@end
//  TableBackgroundTestViewController.h
#import 

@interface TableBackgroundTestViewController : UITableViewController {
    UIImage *backgroundImage;
}

@property (nonatomic, retain) UIImage *backgroundImage;

@end
//  TableBackgroundTestViewController.m
#import "TableBackgroundTestViewController.h"
#import "LadderCell.h"

@implementation TableBackgroundTestViewController
@synthesize backgroundImage;

- (void)dealloc {
    self.backgroundImage = nil;
    [super dealloc];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.backgroundImage = [UIImage imageNamed:@"background.png"];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
    return 1000;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return [LadderCell height];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    LadderCell *cell = (LadderCell *)[tableView dequeueReusableCellWithIdentifier:[LadderCell reuseIdentifier]];
    if (!cell) {
        cell = [[[LadderCell alloc] initWithImage:self.backgroundImage] autorelease];
    }
    [cell setIndexPath:indexPath];
    cell.titleLabel.text = [NSString stringWithFormat:@"Row %d", indexPath.row];
    return cell;
}

@end
cduhn
Not a great solution really, each cell having a full copy of the image will cause a lot of unnecessary memory consumption, and since `UITableViewCell`s are recycled, there would need to be a lot of code to manage which background to display when a cell comes into view. Using a UIScrollView, however is probably a better idea.
Jasarien
Actually, this doesn't consume excessive memory as long as all your UIImageViews share the same UIImage. I tested this with a 320 x 2719 image, which requires an image buffer of over 3.4 MB. Going from 5 rows to 46 visible rows only consumed an extra 57.4 K, suggesting that the overhead comes from object allocs, not duplicate image buffers. I'll edit and post my code.
cduhn
A: 

Have the UITableView background transparent, and add a UIScrollView behind it with the UIImageView inside it. Add a listener for when the UITableView scrolls (since it is a subclass of UIScrollView it has all the same delegate methods). Then, when it scrolls, set the scroll position of the UIScrollView behind it to the same programmatically.

You could technically do it without a second UIScrollView behind the UITableView, just with a plain UIIImageView, if you want to reverse the offset values.

Ed Marty
Which delegate method would that be? I looked at the scorllViewDelegate and found nothing that would be called as it scrolls. Also, would there be a huge performance problem given that you'll be readjusting the image positions many many times a second?
erotsppa
There's really not a performance issue, no. You're not redrawing anything, so you should be fine. As for the delegate method, how about scrollViewDidScroll:
Ed Marty
A: 

My suggestion is similar to Ed Marty (what he suggests "without a second UIScrollView"):

Place the UITableView inside a simple UIView. Make the cell backgrounds transparent so background from below the tableview would show

Below the UITableView, place an UIImageView with your desired background. Both the UITableView and UIImageView now sit inside the same enclosing UIView.

Listen to scroll events of UITableView. When detecting a scroll, simply change the background UIImageView position (frame.origin.y) appropriately, so that it would "stick with" the tableview.

You can have the background as one gigantic image, or have a series of them so you do tiling. You can have an array of the images and add to the array from top/bottom when needed, and also remove the images that have "scrolled away" from screen to conserve memory. You will need to calculate the positions for all these background images yourself, but there's nothing complicated in that.

Jaanus
Which delegate method would that be? I looked at the scorllViewDelegate and found nothing that would be called as it scrolls. Also, would there be a huge performance problem given that you'll be readjusting the image positions many many times a second?
erotsppa
scrollViewDidScroll: (http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIScrollViewDelegate_Protocol/Reference/UIScrollViewDelegate.html#//apple_ref/doc/uid/TP40006923-CH3-SW3) is called whenever the tableview scrolls. It may be called many times during scrolling. / Performance has many factors, you should just test on the device to see it for real in your actual app.
Jaanus