Horizontal UIScrollView inside a UITableViewCell - iphone

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.

Related

delete row with button in custom tableview cell

This is how the structure looks like.
--- UIView
---- ScrollView
--- TableView
.
UIView *topView = [[UIView alloc]initWithFrame:CGRectMake(0, -250, 320, 250)];
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 70, 320, 150) style:UITableViewStylePlain];
tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
tableView.delegate = self;
tableView.dataSource = self;
tableView.backgroundColor = [UIColor blackColor];
tableView.separatorStyle = normal;
[tableView reloadData];
[topView addSubview:tableView];
[self.scrollView addSubview:topView];
In the tableview I am working with a custom tableview cell with a button in it. Here is the code for the button in my CellForRowAtIndex
[cell.btnDelete addTarget:self action:#selector(deleteAppointment:) forControlEvents:UIControlEventTouchUpInside];
Now to get the specific row I do this in my deleteAppointment
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview;
UITableView *tableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
NSLog(#"row: %d",indexPath.row);
But it is still giving the following error.
-[ExceptionCell indexPathForCell:]: unrecognized selector sent to instance 0x210a2fa0
Can anyone help me?
its really Simple, in cellForRowAtIndexPath. just tag your button like
cell.button.tag = indexPath.row;
remove the value from array or dictionary (thats your dataSource) in button #selector method
[array removeObjectAtIndex:button.tag];
Now reload the tableView.
[tableView reloadData];
The error says you are trying to call indexPathForCell on ExceptionCell. indexPathForCell is a method on UITableView, not UITableViewCell.
cell.superview is not returning your UITableView as you expect.
What you want when deleting a Row from a TableView is to keep the Intuitive Animation that gives the method DeleteRowsAtIndexPath, I have been around that problem, and finaly found an EASY solution.
As the question asked, we are using a Custom Cell with a Custom UIButton for Deleting the Cell
So, you'll have to use delegate // CallBack.
That following demonstration is for MonoTouch in C#, it's the same in objective C, but methods are named a little bit differently + syntax.
//TableViewController
//CALLBACK Methods WHEN DELETING A ROW
public void DeletedItem (MyObject arrayObject, CustomCell cellInstance)
{
NSIndexPath[] arrayPath = new NSIndexPath[1] { this.myTableView.IndexPathForCell (cellInstance) };
this.myArray.RemoveItem (arrayObject);
this.myTableView.DeleteRows (arrayPath, UITableViewRowAnimation.Fade);
}
[Export("tableView:cellForRowAtIndexPath:")]
public virtual UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
...
cell.FactoryMethodsThatImUsing (myObject.items[indexPath.Row], DeletedItem);
return cell;
}
//CustomCell
public delegate void FOODelegateMethods (MyObject item, CustomCell instance);
public event FOODelegateMethods ItemDeleted;
IN MY Factory Method
if (!(this.ItemDeleted is Delegate)) {
this.ItemDeleted += new CustomCell.FOODelegateMethods (ResultCallBack);
}
And then on the DeleteItem Actions
partial void DeleteItem (NSObject sender)
{
ItemDeleted(arrayObject, this);
}

Creating a custom UITableView

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.

How do I know which content view is touched?

I have two questions about content view.
1st question:
There are two content views in tableview cell. How do I know which one is touched?
2nd question:
I only want content view to appear in the first section of the tableview.
But, when I scroll up tableview, content view appears in the third section also.
How can I fix this problem?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UIImageView *imgView, *imgView1;
if(cell == nil)
{
if (indexPath.section == 0) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.text = #"test";
imgView = [[UIImageView alloc] initWithFrame:CGRectMake(100,0,20,62)];
[imgView setImage:[UIImage imageNamed:#"1.png"]];
imgView.tag = 10;
[cell.contentView addSubview:imgView];
[imgView release];
imgView1 = [[UIImageView alloc] initWithFrame:CGRectMake(200,0,20,62)];
[imgView1 setImage:[UIImage imageNamed:#"2.png"]];
imgView1.tag = 20;
[cell.contentView addSubview:imgView1];
[imgView1 release];
}
}
else
{
if (indexPath.section == 0) {
imgView = (id)[cell.contentView viewWithTag:10];
imgView1 = (id)[cell.contentView viewWithTag:20];
}
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// How do I know left content view is touched or right content view is touched?
}
1) You can add different recognizers to each view which will call different methods.
// create view
UIImageView *view = [UIImageView ...];
view.userInteractionEnabled = YES;
// create recognizer
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(myViewTapped:)];
// add recognizer to your view
[view addGestureRecognizer:recognizer];
[recognizer release];
// now when user will tap on your view method - (IBAction)myViewTapped:(UIGestureRecognizer); will be called
2) As you are reusing cell, don't forget to nil or remove from content view unneedfull views (as they already been added in previous section and reused in third one).
UIView *view2remove = [cell viewWithTag:itsTag];
[view2remove removeFromSuperview];
The code you posted is missing a brace. You need an extra close brace before the else. Presumably your real code is not, or it would not compile.
In the else clause, assigning something to local variables won't do anything.
You also need to do something if indexPath.section != 0. If you do nothing, it is possible you will get the content of a cell that was built earlier. If you want the views not to appear, you would have to remove them. Something like:
for (UIView *subview in cell.contentView.subviews)
[subview removeFromSuperview];
But I think it will be easier if you just use a different cell identifier for section 1 and other sections. Then you will not get back cells that have been used for section 1 in section 3, and have to reconfigure them. Like:
NSString *CellIdentifier;
if (indexPath.section == 0)
CellIdentifier = #"Section1Cell";
else
CellIdentifier = #"OtherSectionCell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

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.

Tab Bar Item Images Invisible in MoreNavigationController with Black Background

I have a Tab Bar (UITabBarController) application with a "More" View Controller because of having more than 5 views. The rest of my application has a black background and white text, and I have been able to customize this table to match this. As a result of this however, the Tab Bar images that normally appear on the left side of the "More" table are invisible (since they're meant for a white background). I need to find a way to make the Tab Bar images display as they do in the Tab Bar which itself has a black background.
Example:
Black Background (Invisible Images)
I have subclassed the TabBarController and changed the data source for the "MoreTableView":
TabBarController.m
- (void)viewDidLoad {
[super viewDidLoad];
UINavigationController *moreController = self.moreNavigationController;
moreController.navigationBar.barStyle = UIBarStyleBlackOpaque;
if ([moreController.topViewController.view isKindOfClass:[UITableView class]])
{
UITableView *view = (UITableView *)moreController.topViewController.view;
view.separatorColor = [[UIColor alloc] initWithRed:0.3 green:0.3 blue:0.3 alpha:1.0];
view.backgroundColor = [UIColor blackColor];
view.dataSource = [[MoreTableViewDataSource alloc] initWithDataSource:view.dataSource];
}
}
MoreTableViewDataSource.m
-(MoreTableViewDataSource *) initWithDataSource:(id<UITableViewDataSource>) dataSource
{
originalDataSource = dataSource;
[super init];
return self;
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
return [originalDataSource tableView:table numberOfRowsInSection:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [originalDataSource tableView:tableView cellForRowAtIndexPath:indexPath];
cell.textColor = [[UIColor alloc] initWithRed:1 green:0.55 blue:0 alpha:1.0];
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"customAccessory.png"]];
return cell;
}
Thanks in advance,
Julian
Doing moreController.topViewController.view is kinda dangerous.
I'd recommend instead putting in a tab of your own creation that does the more list, with the normal icon, then opening the other tabs under the more list via normal nav controller methods. You'll need to remove any overflow tab items from the tab bar of course.
There might be a more elegant way to do this, however.
first, thanks for posting your subclass of the tabbar controller.
I included it into the application im writing and ran into the same problem.
My solution is to simply swap the image and the highlighted image.
For this you have to change the method tableView:cellForRowAtIndexPath: of MoreTableViewDataSource.m, e.g.:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [dataSource tableView:tableView cellForRowAtIndexPath:indexPath];
UIImageView *imageView = [cell imageView];
UIImage *image = [imageView image];
[imageView setImage:[imageView highlightedImage]];
[imageView setHighlightedImage:image];
return cell;
}
Cheers,
Michael