It seems to work much faster to do this:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
JHomeViewCell *cell = (JHomeViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[JHomeViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.cellContent.thumbnailCache = self.thumbnailCache;
}
Entry *entry = [self.resultsController objectAtIndexPath:indexPath];
if (cell.cellContent.entry != entry) {
[cell.cellContent setNeedsDisplay];
}
}
The problem is when an entry is edited, the cell doesn't change. I could check for all elements of the cell, to see if they're different, but is there a better way of doing this? Calling drawrect every time the cell appears slows the app down and seems unnecessary.
If you want to do custom drawing for your tableview cell you have to subclass UITableViewCell (as it looks you have done with JHomeViewCell).
Put drawRect inside of your implementation of JHomeViewCell
#implementation JHomeviewCell
- (void)drawRect:(CGRect)rect
{
[super drawRect];
// Insert drawing code here
}
#end
Also, you shouldn't call drawRect directly. You should call setNeedsDisplay instead, and probably need to set the cellContent.entry value to the entry you have from your results controller.
Entry *entry = [self.resultsController objectAtIndexPath:indexPath];
if (cell.cellContent.entry != entry) {
cell.cellContent.entry = entry;
[cell setNeedsDisplay];
}
Related
I have an NSArray (called masterArray) with 2 different type of objects in it - object1 and object2. These objects require 2 different table view cells (each one made programmatically), I implemented this as such
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[masterArray objectAtIndex:indexPath.row] isKindOfClass:[object1 class]])
{
cell1 *cell = (cell1 *)[tableView dequeueReusableCellWithIdentifier:#"obj1Cell"];
if (cell == nil)
{
cell = [[cell1 alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"obj1Cell"];
}
//set up labels and stuff
return cell;
}
if ([[masterArray objectAtIndex:indexPath.row] isKindOfClass:[object2 class]])
{
cell2 *cell = (cell2 *)[tableView dequeueReusableCellWithIdentifier:#"obj2Cell"];
if (cell == nil)
{
cell = [[cell2 alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"obj2Cell"];
}
//set up labels and stuff
return cell;
}
return nil;
}
Now, this code displays the cells fine. When you tap a cell, I use this code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
newViewController *newVC = [[newViewController alloc]init];
newVC.item = [masterArray objectAtIndex:indexPath.row];
[self.navigationController pushViewController:newVC animated:YES];
}
When I tap however, I get the error message "UITableView failed to obtain a cell from its dataSource". I've looked at other questions answering this too. This one says to add the line
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
so I do so, replacing my "cell1 *cell = (cell1 *)..." line with
cell1 *cell = (cell1 *)[tableView dequeueReusableCellWithIdentifier:#"obj1Cell" forIndexPath:indexPath];
yet now I get a
"'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier obj1cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard"
error, which doesn't apply in my situation because my UITableViewCells are made programmatically (there is no storyboard). Also recommended in that post is to add "UITableViewDelegate, UITableViewDataSource", however this appears to only be an option in Swift, not Objective-C.
Before using dequeueReusableCellWithIdentifier with an ID, you need to register this ID like so:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView = ...;
[self.tableView registerClass:[cell1 class] forCellReuseIdentifier:#"obj1Cell"];
[self.tableView registerClass:[cell2 class] forCellReuseIdentifier:#"obj2Cell"];
}
Additional notes:
if (cell == nil) is redundant, because it is never nil in practice.
In Objective-C the convention is to have class names started from an app prefix (2-3 uppercase letters), and an uppercase letter, e.g.: MYCell1, MYCell2, MYObject1, etc. (replace MY with something meaningful for you).
I want to create a custom tableViewCell programmatically.
This is what I do:
Creating a tableViewCell subclass and importing it to the tableViewController
In the tableViewController m:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"StoreCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
return cell;}
In the CustomCell m:
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
NSLog(#"Hello!");
}
return self;
}
(Sorry for not getting the code highlighting thing to work)
My problem is that the CustomCell don't get initialized. The initWithStyle never gets triggered. I followed a couple of tutorials and they do the exact same thing, but successfully..
With iOS 6, dequeReusableCellWithIdentifier:forIndexPath: always returns a cell, so your if-case never gets called.
If a cell with that identifier is not available, it initializes it itself.
Try implementing initWithCoder: in the UITableViewCell subclass, that's what gets called in that case.
In cellForRowAtIndexPath try this
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"StoreCell";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
return cell;
}
I finally figured it out. The cells didn't get initialized because I was using prototypes cells in the storyboard. I set the prototypes cells to 0 and it works :)
Why does my UITableView not update? Here is how I am attempting to update it.
- (void)viewWillAppear:(BOOL)animated
{
NSArray* arrValues = [self.defaults objectForKey:#"values"];
[self.tableScores insertRowsAtIndexPaths:arrValues withRowAnimation:UITableViewRowAnimationNone];
}
arrValues is now an array of NSNumbers. I am sure that it is not empty.
Call [tableScores reloadData]; in - (void)viewWillAppear:(BOOL)animated
Update 1
Also, you need to define arrValues in your header. Each time the viewWillAppear, you are creating a new instance, but you won't be able to use it throughout the rest of your controller. This is the main reason you aren't seeing anything besides at your breakpoint.
Update 2
According to your comment below, you have not implemented cellForRowAtIndexPath: which is how the cell is created. Below is an example, but you may want to search around the net for example projects because this UITableView's 101. There is still a lot more you need to learn when it comes to arrays and tableViews.
Example cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"FriendCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [arrValues objectAtIndex:indexPath.row];
return cell;
}
I have a case but the detailtext is not showing? Does anyone know what te problem is?
i used: UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIViewController *controller;
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
switch(indexPath.row) {
case 0:
{
NSLog(#"Case 0");
controller = [[wed1 alloc] init]; //WithStyle:UITableViewStylePlain];
[self.navigationController pushViewController:controller animated:YES];
cell.detailTextLabel.text = #"Wedstrijden!";
}
break;
case 0:
{
NSLog(#"Case 0");
controller = [[wed1 alloc] init]; //WithStyle:UITableViewStylePlain];
//created a controller
[self.navigationController pushViewController:controller animated:YES];
//at his point, you are showing next ViewController's view
cell.detailTextLabel.text = #"Wedstrijden!";
//here you are changing the text of the text for current tableView's cell (which is actually going to be off the screen as new controller's view will be shown after previous statement execution)
}
with the piece of code you shared - I don't find anything wrong here, the detail text should not be visible as this view will be gone.
I don't fully understand why you'd want to set the detail text on a cell after pushing a new view controller. As samfisher said, when the user presses the first cell, in case 0, the new view controller will be alloc'd, init'd and then pushed, and after that you set the detail text to "Wedstrijden".
I'm assuming you're probably just confused between which property to use. My question to you is: when and where do you want this text to appear?
If the cell was created with the style UITableViewCellStyleDefault, the detail text label isn't displayed. In tableView:cellForRowAtIndexPath: implementation, you may create the cell with style UITableViewCellStyleSubtitle or UITableViewCellStyleValue1.
Use UIListContentConfiguration instead, or subtitle won't show.
#import <UIKit/UIListContentConfiguration.h>
And in the cellForRowAtIndexPath method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"AddressCell" forIndexPath:indexPath];
if(cell==nil){
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"AddressCell"];
}
NSRange range = [self.dataArray[indexPath.row] rangeOfString:#","];
NSString *street;
NSString *restAddress;
if (range.location != NSNotFound) {
street = [self.dataArray[indexPath.row] substringToIndex:range.location];
restAddress= [self.dataArray[indexPath.row] substringFromIndex:range.location + 1];
}
UIListContentConfiguration *configuration = cell.defaultContentConfiguration;
configuration.text=street;
configuration.image=kImage(#"Location");
configuration.secondaryText=restAddress;
cell.contentConfiguration = configuration;
return cell;
}
Will look like this:
when I used layoutSubviews method of UItableViewcell with category, just like the code below
#implementation UITableViewCell (forimage)
- (void)layoutSubviews{
[super layoutSubviews];
self.imageView.frame = CGRectMake(0, 0, 50, 50);
}
#end
when I used the code below to draw the cells , the textLabel was disappeared ,anyone know why it be that~, and does that mean if I use layoutSubviews,I must write all the subviews what I need in the method?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
RadioInfo *radioinfo = [radios objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#",radioinfo._name];
if(!radioinfo._logo){
if(self.tableView.dragging == NO && self.tableView.decelerating == NO){
[self startPicDownload:radioinfo forIndexPath:indexPath];
}
cell.imageView.image = [UIImage imageNamed:#"1.jpg"];
}
else {
cell.imageView.image = radioinfo._logo;
}
return cell;
}
What you want to do is add to the behaviour of UITableViewCell's layoutSubviews method, not replace it.
To properly add to the behaviour, subclass the cell and perform your own layout, as you have above but add a [super layoutSubviews] right at the top of your method to ensure that the cell's own basic layout is performed first.