Here is the cellForItemAtIndexPath code I have:
static NSString *cellIdentifier = #"Cell";
MyCustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
//do stuff, set label text
if (indexPath.section == 0) cell.label.text = [words1 objectAtIndex:indexPath.row];
else if (indexPath.section == 1) cell.label.text = [words2 objectAtIndex:indexPath.row];
else if (indexPath.section == 2) cell.label.text = [words3 objectAtIndex:indexPath.row];
return cell;
I register the cell type in viewDidLoad:
[_collectionView registerClass:[MyCustomCell class] forCellWithReuseIdentifier:#"Cell"];
I have my collection view setup in my storyboard with delegates and datasource linked up as normal.
Now my issue... I have two approaches, both with problems. I have 3 sections, each drawing from a unique array of data.
After I have fetched the required data, I use [_collectionView reloadData]. This loads the correct number of cells, as per the array count, but the cells offscreen are not populated. So when I scroll them onscreen, they have not has their label set.
Instead of the above, I try reloading each section as the data is fetched (a preferred route) using the following code (once per section):
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:nil];
However, the problem using this method, is that after it has reloaded, it fills my collection view with the correct number of cells, but the cells themselves are blank, no text set on the labels.
In both scenarios, if I scroll the non-loading cells onscreen, offscreen and back on again, they will load. But the first time you see them, they don't.
Resolved. Placing your UI code, such as creating UILabel (not nil) inside the layoutSubviews method is bad.
Moving this code to the init method on my custom UICollectionViewCell subclass, fixed my problem if cell UI not consistently.
Related
I want to change a specific UIImageView inside a UITableViewCell with this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView beginUpdates];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UYLTextCell *textCell = (UYLTextCell *)cell;
textCell.selected = YES;
//get and show the image at selected cell
textCell.testImage.image = [UIImage imageNamed:#"image280280.jpg"];
//[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
}
But what happened is some cell had their image View even though it has not been clicked.
Table views re-use cells. When a cell is scrolled offscreen it is added to a queue, and will be re-used for the next cell to be scrolled onscreen. That means your cell configuration code in tableView:cellForRowAtIndexPath: should be enough to properly configure any cell, including cells that were previously selected at a different indexPath. In tableView:didSelectRowAtIndexPath: store the selected index path in a property and use that property to configure your cell in tableView:cellForRowAtIndexPath:. The image will need to be set in tableView:cellForRowAtIndexPath: whether the cell is selected or not (unless you implement prepareForReuse in your custom cell).
I am trying to create a project with a custom UITableViewCell. The custom cells never load, they're just blank. At this point in the project what I'm trying to do is placing a UITableViewCell in a .xib, designing it the way I want and specifying its reuse identifier along with tag IDs for the components so that I can use them in code later on.
I've googled a ton and found several tutorials that look like what I want to do, along with many SO questions that have answers that seem applicable. At this point it's probably just my head spinning with too many different angles and solutions.
This is my current attempt at trying to register the custom cell with my UITableView, yet when running this on a device the rows in the table view are entirely blank.
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"];
}
UILabel* l1 = (UILabel*)[cell viewWithTag:1];
UILabel* l2 = (UILabel*)[cell viewWithTag:2];
UILabel* l3 = (UILabel*)[cell viewWithTag:3];
l1.text = #"Foobar";
l2.text = #"Foobar";
l3.text = #"Foobar";
I'm pretty certain that I've hooked up all the properties and such correctly, but at this stage I need a fresh pair of eyes to point out the facepalm for me.
The interesting files are FilmerView.m/h/xib and the cell is in FilmerViewCell.xib. When running the app this TableView is in the second tab of the tab bar controller.
Project:
http://speedy.sh/WhhpP/test12.zip
I can't provide a full answer atm but look up the tableview method. registerNib:forCellReuseIdentifier:
Also, stop using that dequeue method. Use the one that includes the indexPath.
Then you don't have to check if the cell is nil afterwards.
EDIT
In viewDidLoad (or somewhere like that)...
UINib *cellNib = [UINib nibWithNibName:#"MyCustomCellXibFileName" bundle:[NSBundle mainBundle]];
[self.tableView registerNib:cellNib forCellReuseIdentifier:#"CellIdentifier"];
Now in the table view datasource method...
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier" forIndexPath:indexPath];
// no need to check cell == nil.
// this method is guaranteed to return a non nil cell.
// if it doesn't then the program will crash telling you that you need to...
// register a class or nib (but we just did this in viewDidLoad) :D
// configure your cell here...
[self configureMyCell:(MyCustomCell *)cell atIndexPath:indexPath];
return cell;
}
- (void)configureMyCell:(MyCustomCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
cell.nameLabel.text = #"Hello, world";
}
Hope this helps.
Make sure that you have set datasource and delegate properties of your tableView.
Make sure that you have implemented (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method and it returns a positive value (>0).
Evaluate the following:
Is the ReuseIdentifier set in the XIB. See Properties Tab in Interface Builder on the right when selecting the cell.
Are the AutoresizingMasks set properly for the labels to be visible?
WrapModes: Which did you select? When having wrapmode WrapWord, is the font size too large for the text to be moved in the next line becoming invisible?
Set the background color of the UITableViewCellss content view to something else than white or transparent, as well as the background colors of the labels to see if the cell is even there.
Manually call numberOfRowsInSection on your table, pass the proper NSIndexPath identifying the target section and see if its greater 0 to confirm that the TableView even attempts to load the data, thus, the cells. ( Alternatively set a breakpoint in the method or do a NSLog. )
Do a NSLog in cellForRowAtIndexPath to confirm that the cell returned is not nil and the method is even called!
So I've made some custom table view cells and they draw correctly and look great, however once I scroll past the edge of the visible cells they start being reused, which is fine, except that when I scroll back the reused cells are still shown and don't redraw. Specifically all the cells look the same except for the top-most cell.
Pictures detailing the occurrence:
How I have this coded up, is when the cells get made if the indexPath.row is greater than 0 add an "overlap effect" which is just a gradient on a uiview placed underneath the custom drawing on the UITableViewCell's contentView.
This is how I add the overlap effect in the UITableViewController's tableView:cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"APostCell";
PostCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
CustomPost *aPost = [self.posts objectAtIndex:indexPath.row];
if (indexPath.row > 0) {
[cell addOverlap];
}
cell.postDateLabel.text = [aPost datePostedAsString];
return cell;
}
How would I implement this [cell removeOverlap]?
Try this
if (indexPath.row == 0) {
//remove overlap here
} else {
[cell addOverlap];
}
beacuse, except 1st cell all have overlap.On scrolling the reused cell have the overlap. So for first cell remove the overlap if present.
So after I posted the question I figured it out and, since I had the question and had previously not found any information on the subject figured I would share.
So whenever
PostCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]
is called, the table view either creates a new cell or reuses an old one. When a new cell is created and it is not the top cell (indexPath.row == 0) it adds the overlap to the UITableViewCell. And if it reuses the cell, that method still gets called, regardless what cell is being reused. So naturally once the cell created at the top is reused, the gradient view still gets added to cell.contentView and it stays there even when I'm reusing for the topmost cell again.
In fact adding the overlap view in this way will stack multiple overlap views into the same cell.
So what has to be done (if you intend to customize the cell appearance this way) is to remove the added views before each reuse of the cell. So you have to overwrite the custom tableviewcell's prepareForReuse method and do just that like so.
- (void) prepareForReuse {
[super prepareForReuse];
[self removeOverlap];
}
Be SURE the cell has the overlap view otherwise your app will break by trying to remove views not there. so have something like
- (void) removeOverlap {
if ([self.contentView.subviews count] > 1) {
//This method works based on the assumption [cell addOverlap] adds new view
//underneath existing views - like [self.contentView insertSubview:overlappedView atIndex:0];
[[self.contentView.subviews objectAtIndex:0] removeFromSuperview];
}
}
I'm creating an app which contains a screen that shows a table view with custom cells. Each cell contains two labels and a subview, which further contains other subviews. I'm handling the click event on the cell to hide/show the subviews within the subview in the cell. How can I make it so that when I click on a single cell, the subview of all the cells will change?
It is like the Stock application in iPhone (using iOS 7), here is a screenshot:
As in the image above, when you click on any of the green box, all the boxes change to reflect the same type of value.
Please let me know if this approach is fine, or how this can be implemented.
There are a couple ways of doing this. The first that comes to mind would be to handle the different states within the UITableViewCell subclass, and just reload the visible cells:
[self.tableView reloadRowsAtIndexPaths:[self.tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationAutomatic];
If you're looking for more control over the process though, this process could also be achieved by changing the state future cells should load into, and then calling a method on every visible cell. This would provide you with an easy way to have complete control over how the contents of the cell update.
// Change flag for cell state then...
for (NSIndexPath *indexPath in [self.tableView indexPathsForVisibleRows]) {
if (condition) {
MyCellSubclass *cell = (MyCellSubclass *)[self.tableView cellForRowAtIndexPath:indexPath];
[cell someMethodWithArg:(id)state];
}
}
To do something as in Stock app you should handle two method cellForRowAtIndexPath: and click action method.
In cellForRowAtIndexPath: you should do the check which cell/button was pressed and display value base on it:
//Pseudo code
//cellForRowAtIndexPath
if (cellNo3Pressed)
{
//set up text with the right value.
}
else if (otherCell)
{
//set up text with the right value.
}
This will handle the cell which are not visible on the screen.
The next action method should handle nice animation on all of the visible cell:
NSArray *paths = [tableView indexPathsForVisibleRows];
for (NSIndexPath *path in paths)
{
UITableViewCell *cell = (UITableViewCell *)[self.tableView cellForRowAtIndexPath:path];
//Animate changes for cell
}
In my UITableView, I recently changed the structure of the cell from formerly just putting UILabels in the contentView of the cell, to adding two UIViews (CellFront and CellBack, on top of one another) into the contentView (so I can achieve a sliding effect by sliding the top one off and revealing the lower one) and adding the UILabels to the top UIView.
However, now, for whatever reason, the cells never get init'd and as a result my UITableView is full of blank cells.
My cell gets created as follows (ArticleCell is a subclass of UITableViewCell):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = nil;
ArticleInfo *articleInfo = [self.fetchedResultsController objectAtIndexPath:indexPath];
// Checks if user simply added a body of text (not from a source or URL)
if ([articleInfo.isUserAddedText isEqualToNumber:#(YES)]) {
CellIdentifier = #"BasicArticleCell";
}
else {
CellIdentifier = #"FullArticleCell";
}
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[ArticleCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// If the user simply added a body of text, only articlePreview and progress has to be set
cell.preview = articleInfo.preview;
// If it's from a URL or a source, set title and URL as well
if ([articleInfo.isUserAddedText isEqualToNumber:#(NO)]) {
cell.title = articleInfo.title;
cell.URL = articleInfo.url;
}
return cell;
}
But I set a breakpoint on the initWithStyle method above within the if statement and it never gets called:
What would cause this? I'm deleting the app and building it from scratch every time, so data is definitely being added to the UITableView, but all the cells are blank. And I can tell a bunch of cells are being added as I have disclosure indicators on all of them, and the table view just gets filled with empty cells with the indicators only.
What am I doing wrong?
try
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
instead of
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
the first one is the old standard way. It will not create a cell for you. While with the second a cell will be created form the storyboard. So if you use storyboards you should use indeed the method you are using now, but it will never go info the if branch, as the cell will never be nil.
when instantiating form storyboard, initWithStyle:reuseIdentifier: is never called. Either set everything up in -initWithCoder: or -layoutSubviews