Creating a custom UITableView - iphone

I am trying to create a custom UITableView that is contained within another view. I have been able to get the view set up almost like the way that I want it. I am just wondering however how come the two methods (tableView:cellForRowAtIndexPath and tableView:didSelectRowAtIndexPath) that I need the most are not getting called. Note that I am not using UITableViewController, but I am using UIViewController to control the tableView.
And here is a snippet of my code
- (void)loadView {
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
UIView * contentView = [[UIView alloc] initWithFrame:applicationFrame];
contentView.backgroundColor = [UIColor whiteColor];
self.view = contentView;
UIImageView * backgroundImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mainBackground.jpeg"]];
[self.view addSubview:backgroundImageView];
CGRect tableFrame = CGRectMake(40, 40, 240, 310);
self.listView = [[UITableView alloc] initWithFrame:tableFrame];
self.listView.dataSource = self;
self.listView.delegate = self;
self.listView.scrollEnabled = NO;
self.listView.rowHeight = 50;
self.listView.backgroundColor = [UIColor clearColor];
self.listView.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"cellBgMid"]];
[self.view addSubview:self.listView];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"cellRow %#", indexPath.row);
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Did Select Row%#", indexPath.row);
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}

Did you implement the -tableView:numberOfRowsInSection: ? Without it you won't be able to have any cells in your table view.

You need to implement the tableviewdelegate and tableviewsource protocols. You will need to add an IBOutlet to UITableView that is the reference outlet in xib for the table vie and set the data source as the your UIView controller once you add the protocols.
Sorry I see your not using xib. It's curious your not getting a warning on the line where your setting the data source and delegate, so you may already have your protocols there.

Related

UITableview Crash when scroll up or down and how to set horizontal scroll view to every cell

I want to display table view in which every cell contain nested UIview but i am facing some problems.
1) My app crash when i try to scroll table view.
Solved by add root view instead of subview into app delegate
Now problem number 2
2) I want to add horizontal scroll view inside table cell.so i can display 3 subview in single cell and i can scroll horizontally with in cell..how can i do that.
I want to do this..
To achive this i have code this..
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
UIView *wagon = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 430, 187)];
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"wagon.png"]];
wagon.backgroundColor = background;
UIScrollView *scrollview = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, 830, 187)];
scrollview.showsVerticalScrollIndicator=YES;
scrollview.scrollEnabled=YES;
scrollview.userInteractionEnabled=YES;
UIView *videoview = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 220 , 100)];
UIImageView *video = [[UIImageView alloc]initWithFrame:CGRectMake(wagon.frame.origin.x+18, wagon.frame.origin.y+35, 220, 100)];
UIImage *bgImage = [UIImage imageNamed:#"video.png"];
video.image = bgImage;
videoview.contentMode = UIViewContentModeLeft;
[videoview addSubview:video];
UIView *textview = [[UIView alloc]initWithFrame:CGRectMake(wagon.frame.origin.x+238,wagon.frame.origin.y+28, 150 , 187)];
textview.contentMode = UIViewContentModeRight;
UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(28,10, 150 , 87)];
label.text=#"This is testing text for IOS app to check line are aligned or not and to check video image and nested views for UIviews";
label.backgroundColor = [UIColor clearColor];
label.textColor=[UIColor redColor];
label.numberOfLines = 4;
[textview addSubview:label];
[wagon addSubview:textview];
[wagon addSubview:videoview];
[scrollview addSubview:wagon];
[self addSubview:scrollview];
}
return self;
}
and call this view from table cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = #"StateCell";
UITableViewCell *cell;
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Wagon1 *wg=[[Wagon1 alloc]init];
[cell addSubview:wg];
return cell;
}
by adding wg i get one train boggi, wagon view.. i want 2 wagon view and engine at last or first.
** Table crashing problem is solve**
1) to solve crashing problem i search on stackoverflow and i found solution like adding to delegates or change to retain or strong bla bla.. but non of these work for me.
Here is my code. and one more thing i am not use XIB , nib or storyboard..
#interface MainScreenViewController : UIViewController
{
UITableView* table;
NSString * name;
}
#property (strong, retain) UITableView *table;
#property (strong, retain) NSString *name;;
#end
.m file
#interface MainScreenViewController ()
#end
#implementation MainScreenViewController
#synthesize table;
#synthesize name;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:Nil bundle:Nil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
name=[[NSString alloc]init];
name=#"testing of row";
CGRect frame = self.view.frame;
table= [[UITableView alloc] initWithFrame:frame];
table.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
[table setDelegate:self];// app crash here
table.dataSource = self;// here also
[table reloadData];
[self.view addSubview:table];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 8;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return #"test";
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath;
{
return 190;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = name;
return cell;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I tried your code and it is working perfectly for me , I guess problem should be with view controller initialisation , try this Applications are expected to have a root view controller at the end of application launch and let me know.
As you have mentioned
i have added both but still it crash..i got this 2013-04-23 12:37:28.361 AMM[2231:c07] Application windows are expected to have a root view controller at the end of application launch 2013-04-23 12:37:32.533 AMM[2231:c07] * -[MainScreenViewController respondsToSelector:]: message sent to deallocated instance 0x71edb70
In the application delegate file have you added the mainview controller to window's rootviewcontroller? Also could you try with some hardcoded text instead of assigning name to cell text.
Here,
Check this is happening in App Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[MainScreenViewController alloc] initWithNibName:#"MainScreenViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
and in MainScreenViewController.m
#interface MainScreenViewController () <UITableViewDataSource,UITableViewDelegate>
#property (nonatomic, strong)UITableView * table;
#property (nonatomic, strong)NSString * name;
#end
#implementation MainScreenViewController
#synthesize table,name;
- (void)viewDidLoad
{
[super viewDidLoad];
name = [[NSString alloc]init];
name = #"testing of row";
CGRect frame = self.view.frame;
table = [[UITableView alloc] initWithFrame:frame];
table.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
[table setDelegate:self];
[table setDataSource:self];
[self.view addSubview:table];
// Do any additional setup after loading the view, typically from a nib.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 8;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return #"Test";
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath;
{
return 190;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
// cell.textLabel.text = name;
// Add UIScrollView for Horizontal scrolling.
NSArray * colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor greenColor],[UIColor blueColor],[UIColor orangeColor], [UIColor blackColor],[UIColor purpleColor], nil];
UIScrollView * scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0.0,0.0,320.0,190.0)];
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * colors.count,scrollView.frame.size.height);
for (int i = 0; i < colors.count; i++) {
CGRect frame;
frame.origin.x = scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = scrollView.frame.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
subview.backgroundColor = [colors objectAtIndex:i];
[scrollView addSubview:subview];
}
[cell.contentView addSubview:scrollView];
return cell;
}

UITableView: custom header title view doesn't show

I want to display a table with custom header titles.
The table view is attached to a controller class that implements the tableview delegate and data source protocols but is not a subclass of UIViewController because the table is a subview to be displayed above another tableview.
some snippets of my code:
The tableview is created programmatically:
_myListView = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStyleGrouped];
[_myListView setDataSource:self.myListController];
[_myListView setDelegate:self.myListController];
[_myListView setBackgroundColor:darkBackgroundColor];
where myListController is a strong property in the class.
For the number of rows in sections:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
…
return count;
}
The number of sections:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [someDelegate sectionCount];
}
For the custom Header View:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView* headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, SectionHeaderHeight)];
UILabel* sectionHeaderTitle = [[UILabel alloc] initWithFrame:CGRectMake(20, 3, 300, 24)];
[headerView setBackgroundColor:[UIColor clearColor]];
sectionHeaderTitle.text = [self myTitleForHeaderInSection:section];
sectionHeaderTitle.textColor = [UIColor whiteColor];
sectionHeaderTitle.textAlignment = UITextAlignmentLeft;
[headerView addSubview:sectionHeaderTitle];
return headerView;
}
For the custom headerViewHeight (as required since iOS5):
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if ( [self tableView:tableView numberOfRowsInSection:section] > 0) {
return SectionHeaderHeight;
} else {
return 0;
}
}
Sadly, the tableview does not display any section headers just as if I would return nil.
However, I have checked with a breakpoint, that the code actually returns an UIView.
Everything else works fine.
What am I missing? PLease, don't hesitate to make me feel ashamed of my self.
I don't really understand why you want to use a custom view, and not the "standard" one ? You may have your reasons, but I don't see anything in your code telling me why :)
I would personally just use this:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section == 0) return #"First section header title";
if (section == 1) return #"Second section header title";
else return nil;
}
Tell me if that's not what you're looking for !
I seem to have found a solution:
I have created a lazy loading strong property for each header view I want to display. (luckily there are only three)
Now the views are shown.
It seems that the header views got deallocated without the strong references before the table was rendered.
Could it be that there is a connection to the class implementing the table view delegate and data source protocols is not a UIViewController?
Text Color you change it to Black color and check once.
sectionHeaderTitle.textColor = [UIColor blackColor];
you try this code this work on my side :-)
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view=[[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 24)];
UIImage *myImage = [UIImage imageNamed:#"SectionBackGround.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:myImage];
imageView.frame = CGRectMake(0,0,320,24);
UIImage *imageIcon = [UIImage imageNamed:#"SectionBackGround.png"];
UIImageView *iconView = [[UIImageView alloc] initWithImage:myImage];
iconView.frame = CGRectMake(0,0,320,24);
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 24)];
label.text = [tableView.dataSource tableView:tableView titleForHeaderInSection:section];
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont boldSystemFontOfSize:14];
[view addSubview:imageView];
[view addSubview:iconView];
[view addSubview:label];
return view;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 24;
}

Customize header section for UITableViewController

I'll need to customize the header section of a UITableViewController where for each sections a different header text is returned (getting data from datasource as well). This is accomplished using the following:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSArray *temp = [listOfMBeans allKeys];
DLog(#"MBean details: %#", temp);
NSString *title = [temp objectAtIndex:section];
DLog(#"Header Title: %#", title);
return title;
};
This works well and I can see the expected output. However I need to change also the font size of text and after looking at similar questions I've implemented the following:
- (UIView *) tableview:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
DLog(#"Custom Header Section Title being set");
UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 30)] autorelease];
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 30)] autorelease];
label.text = [tableView.dataSource tableView:tableView titleForHeaderInSection:section];
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont boldSystemFontOfSize:14];
[headerView addSubview:label];
return headerView;
}
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 44.0;
}
However it seems that the code is never called. My understanding was that UITableViewController is setting by default itself as delegate but it seems I'm wrong.
The UITableViewController is created in this way (as part of hierarchical data):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ProjectDetails *detailViewController = [[ProjectDetails alloc] initWithStyle:UITableViewStyleGrouped];
detailViewController.project = [listOfMetrics objectAtIndex:indexPath.row];
// Push the detail view controller.
[[self navigationController] pushViewController:detailViewController animated:YES];
[detailViewController release];
}
What changes, I should make to make this working?
Thanks.
This question is an older one but I wanted to share my code. I'm using a usual table cell view for my section headers. I have designed it with interface builder and implemented the following delegate method.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: #"Header"];
cell.textLabel.text = #"test";
return cell;
}
You can set explicitly the delegate:
detailViewController.tableView.delegate = detailViewController;
Or you can do it in the controller initial function.
EDIT: your init method should conform to the canonical init. Furthermore, it seems to me that you have not created your UITableView. Try and use this code:
- (id)initWithStyle:(UITableViewStyle)style {
if ((self = [super initWithStyle:style])) {
self.tableView = [[[UITableView alloc] initWithFrame:self.view.bounds] autorelease];
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth UIViewAutoresizingFlexibleHeight;
self.tableView.delegate = self;
}
return self;
}
Of course, you could also do all of this in a nib file...
Here is how you get the barebones section view up using the UITableViewDelegate methods:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 40.0)];
header.backgroundColor = [UIColor grayColor];
UILabel *textLabel = [[UILabel alloc] initWithFrame:header.frame];
textLabel.text = #"Your Section Title";
textLabel.backgroundColor = [UIColor grayColor];
textLabel.textColor = [UIColor whiteColor];
[header addSubview:textLabel];
return header;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 40.0;
}
You could try this: In your ProjectDetails.h declare a UIView *tableHeader and also an accessor method - (UIView *)tableHeader;. Then in the implementation file:
- (UIView *)tableHeader {
if (tableHeader)
return tableHeader;
tableHeader = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 30)];
// addlabel
return tableHeader;
}
In viewDidLoad, call: self.tableView.tableHeaderView = [self tableHeader];
I don't believe you'll need to use the heightForHeaderInSection method.

TableView Row unable to Select

I defined my own controller with no nib file like this:
#interface EngineViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
EngineViewProperties* viewProp;
UIImageView *imgView; // image view of selected engine
NSUInteger selectedIndex;
UITableView *menu;
}
#property (nonatomic,retain) EngineViewProperties* viewProp;
- (EngineViewController *) initWithEngineViewProperties: (EngineViewProperties *) _viewProp;
- (void) dropdownMenu: (id) sender;
I created my view in loadView,with three subviews. The subview arrowBtn is helped to popup a list of search engines.
- (void)loadView {
// ...
UIButton *arrowBtn = [[UIButton alloc] initWithFrame:rect];
[arrowBtn setImage:viewProp.arrowImg forState:UIControlStateNormal];
[arrowBtn addTarget:self action:#selector(dropdownMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:imgView];
[self.view addSubview:label];
[self.view addSubview:arrowBtn];
// ...
}
I create a table listing search engines in the selector dropdownMenu:
- (void) dropdownMenu: (id) sender {
UIButton *arrowBtn = (UIButton *)sender;
// ...
menu = [[UITableView alloc] initWithFrame:rect style:UITableViewStylePlain];
menu.delegate = self;
menu.dataSource = self;
menu.backgroundColor = [UIColor blackColor];
menu.allowsSelection = YES;
[self.view addSubview:menu];
[self.view bringSubviewToFront:menu];
[menu release];
}
And I implemented
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
as usual.
But the result is that the menu popup happily,but I can do nothing with the cells.I clicked the cells,but no response.Those methods like "didSelectRowAtIndexPath" can not be called.
Sorry to paste up so much codes one time.But I really need help.I don't know what is the problem.Please forgive me for my poor English and low development skill in Iphone.And thanks a lot if you give me a little suggestions.
//Added-------------------------
"numberOfRowsInSection" is simple:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [viewProp.txtArray count];
}
and another method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MenuItems = #"MenuItems";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MenuItems];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: MenuItems] autorelease];
}
NSUInteger row = [indexPath row];
cell.imageView.image = [viewProp.imgArray objectAtIndex:row];
cell.textLabel.text = [viewProp.txtArray objectAtIndex:row];
cell.textLabel.textColor = [UIColor whiteColor];
cell.textLabel.font = [UIFont systemFontOfSize:12.0];
cell.selectionStyle = UITableViewCellSelectionStyleNone; // Blue style tried,helpless too
if (row == selectedIndex) {
cell.selected = YES;
}
cell.userInteractionEnabled = YES;
return cell;
}
// Added
Strangely,when the menu firstly poped up,the default selected cell was highlighted correctly,but very quickly the highlighting disappeard.
Check if you have implemented this
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
The didSelectRowAtIndexPath will not called if this is present. One way to workaround this is set [self.view addGestureRecognizer:tap]; towards your targeted view only such as [self.svContent addGestureRecognizer:tap];.
Alternatively, check which view is touched.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ((touch.view == YourTable))
{
return NO;
}
return YES;
}
Try giving cell.selectionStyle = UITableViewCellSelectionStyleBlue in your table View data source. Also check if the userInteraction is enabled for the table view and the cells.
Are you showing any data in the table have you implemented
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
in you viewController??
Seems that you are setting the delgate property correctly, also you added the protocols to the same ViewController. So the problem can be either there is no data in the tableView for selection, or there is some View on top of tableView which is blocking you interaction with the tableView.
Use
cell.selectionStyle = UITableViewCellSelectionStyleGray;
in your
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
for your cell.
remove
cell.userInteractionEnabled = YES;
code from your file.

Horizontal UIScrollView inside a UITableViewCell

I'm trying to create the exact same effect as in the AppStore app for viewing screenshots: inside a UITableView, I have custom subclasses of UITableViewCell's. One of them is designed to show previews of some product, say 4 images. I want them to show the same way as the AppStore presents screenshots of an app : inside a horizontal UIScrollView with its attached UIPageControl.
So I add my UIScrollView and my UIPageControl to my UITableViewCell, just like that :
#implementation phVolumePreviewCell
- (id)init {
if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kVolumePreviewCellIdentifier]) {
[self setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
[self setSelectionStyle:UITableViewCellSeparatorStyleNone];
previews = [[NSMutableArray alloc] initWithCapacity:4];
for (int i = 0; i < 4; i++) {
[previews addObject:#"dummy"];
}
scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
[scrollView setPagingEnabled:YES];
[scrollView setBackgroundColor:[UIColor grayColor]];
[scrollView setIndicatorStyle:UIScrollViewIndicatorStyleBlack];
[scrollView setShowsHorizontalScrollIndicator:YES];
[scrollView setBounces:YES];
[scrollView setScrollEnabled:YES];
[scrollView setDelegate:self];
[self.contentView addSubview:scrollView];
[scrollView release];
pageControl = [[UIPageControl alloc] initWithFrame:CGRectZero];
[pageControl setNumberOfPages:[previews count]];
[pageControl setBackgroundColor:[UIColor grayColor]];
[self.contentView addSubview:pageControl];
[pageControl release];
}
return self;
}
(note: I'm populating "previews" with dummy data here, just in order to have a [previews count] that works ; I want to view the scrollView's scroll indicator for test purposes only, I'll hide them later on).
My problem is that I can scroll the whole UITableView containing this phVolumePreviewCell (subclass of UITableViewCell), but I can't scroll the UIScrollView. How can I achieve this?
The only information I found is there: http://theexciter.com/articles/touches-and-uiscrollview-inside-a-uitableview.html
But it talks of old SDKs (namely 2.2) and I'm sure there is a more "recent" approach to doing this.
Some precisions :
I can scroll the UIScrollView with two fingers. That's not what I want, I want it to be able to scroll with one finger only.
When I scroll with two fingers, the TableView still intercepts the touches and scroll a little bit to the top or the bottom. I'd like the TableView to stick at its position when I touch the ScrollView.
I believe I have to work out how to intercept touches on my TableView, and if the touch is on the phVolumePreviewCell (custom UITableViewCell subclass), pass it to the cell instead of handling it on the TableView.
For the record, my TableView is created programmatically in a UITableViewController subclass:
#interface phVolumeController : UITableViewController <UITableViewDelegate> {
LocalLibrary *lib;
NSString *volumeID;
LLVolume *volume;
}
- (id)initWithLibrary:(LocalLibrary *)newLib andVolumeID:(NSString *)newVolumeID;
#end
 
#implementation phVolumeController
#pragma mark -
#pragma mark Initialization
- (id)initWithLibrary:(LocalLibrary *)newLib andVolumeID:(NSString *)newVolumeID {
if (self = [super initWithStyle:UITableViewStylePlain]) {
lib = [newLib retain];
volumeID = newVolumeID;
volume = [(LLVolume *)[LLVolume alloc] initFromLibrary:lib forID:volumeID];
[[self navigationItem] setTitle:[volume title]];
}
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kVolumePreviewCellIdentifier];
if (cell == nil) {
cell = [[[phVolumePreviewCell alloc] init] autorelease];
}
[(phVolumePreviewCell *)cell setVolume:volume];
return (UITableViewCell *)cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return kVolumePreviewCellHeight;
}
Thanks for any help!
I have found a workaround.
Recreating from scratch a very simple UITableView (8 rows), and inserting a UIScrollView in just the second row, works perfectly. Nested scroll views (the TableView and the ScrollView) scroll independently as expected. All that was done in a single-file test project, in the AppDelegate.
So... first I thought "this might be a delegation problem", so I told the scrollView that its delegate was the TableView, not my custom TableViewCell subclass. To no avail.
Then instead of resorting to a custom, clean TableViewCell subclass that just adds the ScrollView and the PageControl, I resorted to creating a plain TableViewCell in the TableView's cellForRowAtIndexPath: method. Then, I create a UIScrollView right after initializing the TableViewCell, then add it to the TableViewCell, and shazam... it works!
Before:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kVolumePreviewCellIdentifier];
if (cell == nil) {
cell = [[[phVolumePreviewCell alloc] init] autorelease];
}
[(phVolumePreviewCell *)cell setVolume:volume];
// That does the job of creating the cell's scrollView and pageControl
return (UITableViewCell *)cell
After:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kvcPreviewRowIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kvcPreviewRowIdentifier] autorelease];
previewScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, kvcPreviewScrollViewHeight)];
[previewScrollView setContentSize:CGSizeMake(1000, kvcPreviewScrollViewHeight)];
[[cell contentView] addSubview:previewScrollView];
previewPageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, kvcPreviewScrollViewHeight, tableView.frame.size.width, kvcPreviewPageControlHeight)];
[previewPageControl setNumberOfPages:4];
[[cell contentView] addSubview:previewPageControl];
}
return (UITableViewCell *)cell;
How the heck comes that when I create my scrollView from the TVCell subclass, it doesn't play nice ; but when I create the scrollView from the TableView right after creating a "classical" TVCell, it works?
I might have found a solution, but I'm still puzzled by the reason.