Table Cell not always showing Hidden Content after Unhiding - ios

I have a table with a custom cell, in the cells are two UIViews the top one is always displayed and the bottom one is hidden.
When the cellCellForRowAtIndexPath is called the cells are all reduced in height to just show the top UIView.
When the user clicks the cell then that cell is expanded and then I unhide the second UIView. The problem is that sometimes the second UIView does not appear on first click on the cell.
Clicking the cell again makes it all disappear and then another click and it always appears perfectly. Scroll down and then pick another and first click it may not appear or will be in wrong place, two more licks and it is perfect.
I think it is a ReusableCell issue but I have no idea how to get around it.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cellID = #"Cell";
customCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
// Test to see if the Cell has already been built
if (cell == nil)
{
cell = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
// Check if the selected Cell is being drawn and unhide the pull down view and adjust y origin
if(indexPath.row == selectedCell)
{
// Unhide the view
cell.pullDownView.hidden = NO;
CGRect tempFrame = cell.pullDownView.frame;
tempFrame.origin.y = [[secondViewY objectAtIndex:indexPath.row] floatValue];
cell.pullDownView.frame = tempFrame;
// Image in second view
cell.mainImage2.image = [theImages objectAtIndex:indexPath.row];
}
else
{
cell.pullDownView.hidden = YES;
}
// Image in first view
cell.mainImage.image = [theImages objectAtIndex:indexPath.row];
return cell;
}
So clicking a cell calls the didSelectRow which looks like this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectedCell == indexPath.row)
{
selectedCell = -1;
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
else
{
selectedCell = indexPath.row;
}
// [tableView reloadData];
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
[tableView endUpdates];
}
I have tried both reloadData and BeginUpdates as you can see but this still does not work. Is this because I am hiding/unhiding should I do Alpha to 0 and then 1 when selected? Is this a reusable cell thing?
Ohhh for completeness. You may notice I have a table where all the heights are different which is why I change the Y of the second UIView.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
float returnHeightValue;
if(indexPath.row == selectedCell)
{
returnHeightValue = [[selectedCellHeights objectAtIndex:indexPath.row] floatValue];
}
else
{
returnHeightValue = [[normalCellHeights objectAtIndex:indexPath.row] floatValue];
}
return returnHeightValue;
}

Okay I have changed how I address the items. I have removed the UIView (which they were all embedded in) and instead I now have in my .h of my custom cell.
#property (nonatomic, strong) IBOutletCollection(UILabel) NSArray *pullDownItems;
#property (weak, nonatomic) IBOutlet UIImageView *mainImage;
Rather than
#property (weak, nonatomic) IBOutlet UIView *pullDownView;
Okay so now I have to unhide and move two items rather than one but that is way better than ding them individually would have been a pain (there is around 10 UILabels). I have enumerate the array like so;
for(UILabel *obj in cell.pullDownItems)
{
obj.hidden = NO;
tempFrame = obj.frame;
tempFrame.origin.y = tempFrame.origin.y - tempYOffset;
obj.frame = tempFrame;
}
Any new items I just just attach.

Okay changed it all around as hiding and unhiding content on a cell that changed did not work so well (got artifacts left in the table and sometimes things would not show up).
I am now creating two custom cells one has all the additional objects when the cell is selected the other does not.
So far seems to be much better way of doing it.

Related

Hide Label from prototype cell in TableView

I am working on Prototype TableView Cell where I have two Label over cell and one button behind the cell. I want to hide the second label on Hide Button click. I have tried a lot but did not get any appropriate info, Here is my code for displaying the Labels in Cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = #"TableCell";
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
// Configure the cell...
int row = [indexPath row];
cell.EnglishLable.text = _English[row];
cell.UrduLable.text = _Urdu[row];
return cell;
}
Everything is fine, I just need to hide 'UrduLabel' in my Hide IBAction method.
- (IBAction)Hide:(id)sender {
static NSString *simpleTableIdentifier = #"TableCell";
UITableView *tableView = [[UITableView alloc]init];
CGPoint location = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:IndexPath];
BOOL *hideLabel;
hideLabel = YES;
[self.tableView reloadData];
UILabel *subtitle = (UILabel *)[cell.contentView viewWithTag:1];
subtitle.hidden = hideLabel;
}
Whats wrong in my Button method?
Try this code
- (IBAction)Hide:(id)sender {
CGPoint location = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
TableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.UrduLable.hidden = YES;
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:<any row animation you need>];
UILabel *subtitle = (UILabel *)[cell.contentView viewWithTag:1];
subtitle.hidden = hideLabel;
}
You just need to get the particular cell of which the button is clicked, hide the required label and reload the particular cell. And main thing is you need to get an instance of your prototype cell class, not of UITableViewCell
IN CASE ONE BUTTON IN THE VIEWCONTORLLER FOR ALL CELL
Incase of single button toggling the visibility of the UrduLabel, declare the -(IBAction)hide:(sender)id in the yourviewcontroller.m. connect the IBAction of the button from nib/storyboard to the -(IBAction)hide:(sender)id method.
Then follow the following steps
declare a BOOL property variable or ivar inside the view controller. named willShowUrdu.
implement the hide as following:
-(IBAction)hide:(id)sender
{
willShowUrdu = !(willShowUrdu);
[yourTableView reloadData];
}
inside the cellForRowAtIndexPath:
TableViewCell *cell = //whatever the way you get the cell
cell.UrduLabel.hidden = !willShowUrdu;
Thats how toggle the visibility of all UrduLabel under one IBAction.
IN CASE OF EACH BUTTON IN EACH CELL
There is a better way to de-centralize the control to each cell. Move the -(IBAction)hide:(sender)id method to the implementation in the TableViewCell's implementation.
Add the IBAction from nib/storyboard to the moved -(IBAction)hide:(sender)id method.
Take a IBOutlet of the UrduLabel, as seen in above code, it's already taken.
Then the implementation of -(IBAction)hide:(sender)id would be following:
-(IBAction)hide:(sender)id
{
//put your other logic here (if any)
self.UrduLabel.hidden = YES;
}
Welcome to IOS, Happy coding.

First 5 cells not showing image in custom TableView Cell - iOS

I have a UITableView which I have transformed into horizontal tableview, and a custom UITableViewCell which just has a UIImageView and a UILabel
The problem is, first 5 cells don't show the images, but when I scroll and come back to them, images are shown. No idea what the problem is :(
(picture below, please see the horizontal tableview, ignore the vertical one)
Here's my code in TableViewController Class:
-(void)viewDidLoad
{
//Rotating the tableview angle -PI/2 Rad = -90 Degrees
_friendsTableView.transform= CGAffineTransformMakeRotation(-M_PI_2);
_friendsTableView.translatesAutoresizingMaskIntoConstraints = YES;
CGRect frame = _friendsTableView.frame;
frame.origin.y= _segmentControl.frame.size.height;
frame.origin.x= 0;
frame.size.width = self.view.frame.size.width;
frame.size.height = 105.5;
_friendsTableView.frame= frame;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FriendsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (nil == cell) {
cell = [[FriendsTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
}
if(cell)
{
cell.user = cellsArray_[indexPath.row];
cell.transform =CGAffineTransformMakeRotation(M_PI_2);
}
return cell;
}
Now this is my custom cell class code where I set the image and label (_user is a property, so this setter method gets called automatically from cell.user):
-(void)setUser
{
_profileImageView.layer.cornerRadius = _profileImageView.frame.size.width/2;
_user = user;
_nameLabel.text = #"Hello";
[_profileImageView setImage:[UIImage imageNamed:#"placeholder_image"]];
}
Why dont you use Collection View controller instead. Transforming Tableview controller seems not good.
In your code your are not calling your setUser method.
For Custom table view cell you can add following code in
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:strID];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
}
For my experience this is strictly related to the reusability of the Cells, i had a similar problem once, my solution is before assigning something to an IBOulet, make sure to have it empty, after that, assign it and it should work.

sliding UITableView over image

The user first sees an image at the top of the page; the rest of the page is a UITableView directly below the image.
When the user slides up on the table, I'd like to slide the entire UITableView up so that it covers the image, and then start scrolling the table cells. Sliding down (once the first cell is at the top) the UITableView would then slide down to reveal the image again.
This is similar to what the Crackle app (and other apps) do. What is a good / elegant way to do this?
Change the position of TableView by setting its frame,
self.tableView.frame=self.view.frame in
-scrollViewWillBeginDragging:
There are undoubtedly several ways to accomplish this. On way involves having that image inside the first cell, which would be different than the other cells with your data. When you scroll, the position of that image in the cell would be moved down, which would make it appear that the image is being covered by the cell below. The code below shows how to do this (I modified this from another project that had alternating rows that appeared to float over fixed images).
#interface TableController ()
#property (strong,nonatomic) NSArray *theData;
#end
#implementation TableController
- (void)viewDidLoad {
[super viewDidLoad];
self.theData = #[#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight",#"Nine",#"Black",#"Brown",#"Red",#"Orange",#"Yellow",#"Green",#"Blue"];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
RDCell *topCell = (RDCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
[topCell updateImageViewWithOffset:scrollView.contentOffset.y];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.theData.count + 1;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat height = (indexPath.row == 0)? 140 : 44;
return height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
RDCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ImageCell" forIndexPath:indexPath];
return cell;
}else{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = self.theData[indexPath.row - 1];
return cell;
}
}
The code in the custom cell was just this one method,
-(void)updateImageViewWithOffset:(CGFloat) offset {
CGFloat cellY = self.frame.origin.y;
self.topCon.constant = offset - cellY;
}
topCon is an IBOutlet to an NSLayoutConstraint, between the top of the cell and the top of the image view in the cell -- it's important the the image view have constraints to the top and sides of the cell, and a height constraint (equal to the height of the cell), but no constraint to the bottom of the cell. If you want to see if this approach gives you the look you want, you can download the sample project from here, http://jmp.sh/zOVz6Ev.

Managing UITableViewCell selected states

I have UITableViewController with a static table view and 2 sections. The first section has two cells, with a layout Table View Cell > Content View > Text Field.
The second section of table view cells has a layout that is Table View Cell > Content View > Label - and the cells expand when they're selected by using beginUpdates and endUpdates and reporting cell heights via tableView:heightForRowAtIndexPath:.
Right now, when I touch one of the first sections' cells, the keyboard comes up and the cursor is placed in the text field, but the cell is not selected - which means that if I had previously selected one of the cells in the second section, it would remain selected (and therefore expanded).
Also, if I have selected a cell in the first section with the text field given first responder, then I select a cell in the second section, the cell expands, but the first cell does not resign first responder and dismiss the keyboard.
Did I layout my table view incorrectly such that the cell selected states are not managed automatically and if not: what is the proper way to manage the selected states of the UITableViewCells?
I dont know how you have written the code. But from above information I have created one sample application. The code is as below and it is working as you want.
I have considered the table with 2 section.
First section has the textfield on Cell.
Second section has the UIlabel on the cell.
txtFieldCopy is of type UITextField declared in .h of class.
Please check the way I have assigned the Tag to the view as we need it to get the indexpath.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCellStyle style =UITableViewCellStyleSubtitle;
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
int iTag=[indexPath row]+1;
txtFieldCopy=nil;
if(indexPath.section==1)
{
UILabel *lblFirst=nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:MyIdentifier];
lblFirst = [[UILabel alloc] initWithFrame:CGRectMake(45, 0, 100,
40)] ;
lblFirst.tag=iTag;
[cell.contentView addSubview:lblFirst];
lblFirst=nil;
}
lblFirst = (UILabel *)[cell.contentView viewWithTag:iTag];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
lblFirst.text =#"Label 1";
}
else
{
UITextField *txtFirst=nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:MyIdentifier];
txtFirst = [[UITextField alloc] initWithFrame:CGRectMake(40, 0, 50,
40)] ;
txtFirst.tag=iTag;
txtFirst.delegate=(id)self;
[cell.contentView addSubview:txtFirst];
txtFirst=nil;
}
txtFirst = (UITextField *)[cell.contentView viewWithTag:iTag];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
txtFirst.text =#"text 1";
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(txtFieldCopy)
{
[txtFieldCopy resignFirstResponder];
txtFieldCopy=nil;
}
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
NSLog(#"%d",textField.tag);
NSIndexPath *iRowId =[NSIndexPath indexPathForRow:(textField.tag-1) inSection:0];
[tblSample selectRowAtIndexPath:iRowId animated:NO scrollPosition:UITableViewScrollPositionMiddle];
txtFieldCopy=textField;
}
Make sure if suppose in between you are reloading the table view then txtFieldCopy=nil; in the cellForRowAtIndexPath get called and set to null. so you can ignore this line. But before please check whether you are calling it again and again.
Let me know if it worked for you or not. I am sure it definitely work for you

Expanding and Collapsing table view cells in ios

I have a table view of custom cells and some buttons in each cell.Clicking on any of the button inside the cell will reveal another custom view below that cell.Next click on the same button will collapse the view and need this same for all cells.I tried with insertrow method on the button click but in vain.How can i do this with using only the table view delegates.
This is what i tried:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"CustomCell_For_Dashboard";
CustomCellFor_Dashboard *customCell = (CustomCellFor_Dashboard *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (customCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCellFor_Dashboard" owner:self options:nil];
customCell = [nib objectAtIndex:0];
}
[customCell.howyoulfeelBtn addTarget:self action:#selector(buttonclicked:) forControlEvents:UIControlEventTouchUpInside];
customCell.nameLabel.text = #"test";
customCell.imgView.image = [UIImage imageNamed:#"Default.png"];
// customCell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
return customCell;
}
-(void)buttonclicked:(id)sender{
NSIndexPath *indexPath = [myTable indexPathForCell:sender];
[myTable beginUpdates];
NSIndexPath *insertPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
[myTable insertRowsAtIndexPaths:[NSArray arrayWithObject:insertPath] withRowAnimation:UITableViewRowAnimationTop];
}
can anyone help me?
I got the same task on one project with just one thing different: There were no buttons, just tapping on cell will expand or collapse it.
There are several things you should edit in your code. First, the button method code will look something like this:
- (void) collapseExpandButtonTap:(id) sender
{
UIButton* aButton = (UIButton*)sender; //It's actually a button
NSIndexPath* aPath = [self getIndexPathForCellWithButtonByMagic:aButton];
//expandedCells is a mutable set declared in your interface section or private class extensiont
if ([expandedCells containsObject:aPath])
{
[expandedCells removeObject:aPath];
}
else
{
[expandedCells addObject:aPath];
}
[myTableView beginEditing];
[myTableView endEditing]; //Yeah, that old trick to animate cell expand/collapse
}
Now the second thing is UITableViewDelegate method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([expandedCells containsObject:indexPath])
{
return kExpandedCellHeight; //It's not necessary a constant, though
}
else
{
return kNormalCellHeigh; //Again not necessary a constant
}
}
Key thing here is to determine if your cell should be expanded/collapsed and return right height in delegate method.
Going off of what #eagle.dan.1349 said, this is how to do it on the clicking of the cell. In storyboard, you also need to set the table cell to clip subviews, otherwise the content that would be hidden will show.
.h
#property (strong, nonatomic) NSMutableArray *expandedCells;
.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self.expandedCells containsObject:indexPath])
{
[self.expandedCells removeObject:indexPath];
}
else
{
[self.expandedCells addObject:indexPath];
}
[tableView beginUpdates];
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat kExpandedCellHeight = 150;
CGFloat kNormalCellHeigh = 50;
if ([self.expandedCells containsObject:indexPath])
{
return kExpandedCellHeight; //It's not necessary a constant, though
}
else
{
return kNormalCellHeigh; //Again not necessary a constant
}
}
Saw this post and just wanted to give my 2 cents as my solution to this is very similar to the chosen answer (the tapping of a whole area).
Many people architect this by using just cells alone, but I believe there is a way to build this that might align better with what people are trying to achieve:
There are headers and there are cells. Headers should be tappable, and then cells underneath the headers would show or hide. This can be achieved by adding a gesture recognizer to the header, and when tapped, you just remove all of the cells underneath that header (the section), and viceversa (add cells). Of course, you have to maintain state of which headers are "open" and which headers are "closed."
This is nice for a couple of reasons:
The job of headers and cells are separated which makes code cleaner.
This method flows nicely with how table views are built (headers and cells) and, therefore, there isn't much magic - the code is simply removing or adding cells, and should be compatible with later versions of iOS.
I made a very simple library to achieve this. As long as your table view is set up with UITableView section headers and cells, all you have to do is subclass the tableview and the header.
Link: https://github.com/fuzz-productions/FZAccordionTableView
I also had a same situation and my solution was to put a button on top of the Section Title with viewForHeaderInSection method.
noOfRows defines how many rows are there in each section and button.tag keeps which button of section is pressed.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIButton *btnSection = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, tableView.frame.size.height)];
btnSection.tag = section;
[btnSection setTitle:[sectionArray objectAtIndex:section] forState:UIControlStateNormal];
[btnSection addTarget:self action:#selector(sectionButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
return btnSection;
}
- (void)sectionButtonTapped:(UIButton *)button {
sectionIndex = button.tag;
if (button.tag == 0) {
noOfRows = 3;
} else if (button.tag == 1) {
noOfRows = 1;
} else if (button.tag == 2) {
noOfRows = 2;
}
[self.tableView reloadData];
}
Hope this will help you..

Resources