When the tableview appears, at first there are no images. But when I drag it, the images in the tableviewcells will appear. I am using a custom tableviewcell class.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UserCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) {
cell = [[UserCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
}
NSLog(#"%d",indexPath.row);
[cell setUser:_chatContent[indexPath.row]];
[cell initUI];
return cell;
}
This is my custom cell class:
- (void)awakeFromNib {
[super awakeFromNib];
}
-(BOOL)initUI{
BOOL isSuccess;
self.userImageView = self.user.userImageView;
UIView *superView = self.contentView;
[superView addSubview:self.userImageView];
[self.userImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superView.mas_top).with.mas_offset(8);
make.bottom.equalTo(superView.mas_bottom).with.mas_offset(-8);
make.left.equalTo(superView.mas_left).with.mas_offset(8);
make.width.equalTo(superView.mas_height).with.mas_offset(-16);
}];
self.userImageView.layer.cornerRadius = self.userImageView.frame.size.width/2;
self.imageView.clipsToBounds = YES;
isSuccess = YES;
return isSuccess;
}
Finally, this is my imageview code:
User *user = _chatContent[i];
NSString *fileName = [NSString stringWithFormat:#"%d",i];
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:#"jpg"];
user.userImageView = [[UIImageView alloc] initWithImage: [UIImage imageNamed:filePath]];
Set the cell's imageView.image to nil before setting image for imageView because tableView reloads cells every time it is scrolled.
After setting imageView.image call setNeedsDisplay and reloadInputViews of UITableViewCell to set image immediately.
[cell reloadInputViews];
[cell setNeedsDisplay];
Hope it helps.
Related
I have tableview in one of my controller. It is not loading data into custom labels and image view with tags. I checked each and every thing, delegates are attached and I am reloading the data in viewWillAppear. I do not understand, where is the problem. I added label and images and assign them tag. Only default label of tableview is showed.
NSString *cellIdentifier;
NSMutableArray *historyArray;
#implementation CloudHistory
-(void)viewDidLoad
{
[super viewDidLoad];
historyArray = [[NSMutableArray alloc]initWithObjects:#"history1",#"history2",#"history3",#"history4",nil];
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
dispatch_async(dispatch_get_main_queue(), ^{
//Reload data in services table.
[_historyTableView reloadData];
});
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//Number of section in services table.
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [historyArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
cellIdentifier = #"HistoryCell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
UILabel *name = (UILabel*)[cell viewWithTag:40];
name.text =[historyArray objectAtIndex:indexPath.row];
UIImageView *image = (UIImageView*)[cell viewWithTag:44];
image.image = [UIImage imageNamed:#"rtb_logo"];
return cell;
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
//Change the background color to stoker Cloud color.
cell.selectedBackgroundView = [UIView new];
cell.selectedBackgroundView.backgroundColor = [ UIColor colorWithRed:116.0/255.0 green:174.0/255.0 blue:220.0/255.0 alpha:1.0];
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
-(BOOL)prefersStatusBarHidden
{
return YES;
}
#end
maybe you can export the name and image , I think some of them maybe is invalid or nil .
UILabel *name = (UILabel*)[cell viewWithTag:40];
NSLog(#"name = %#",name); // is it valid ?
UIImageView *image = (UIImageView*)[cell viewWithTag:44];
NSLog(#"image = %#", image); // is it valid ?
if some of them invalid or nil,you can do something like this:
UILabel *name = [[UILabel alloc] initWithFrame:CGRectMake(0, 5, 80, 44)];
name.text = #"name";
[cell addSubview:name];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 5, 44, 44)];
imageView.image = image;
[cell addSubview:imageView];
hope it helps.
Did you create a custom UITableViewCell using a xib file?
If so, you could register it in viewdidload.
Something like this
[self.tableView registerNib:[UINib nibWithNibName:#"xibName" bundle:nil] forCellReuseIdentifier:CellIdentifier];
Then your tableview can find your custom view outlets.
Hope this helps
Why don't you use a custom UITableViewCell with your UILabel and UIImageView? So you can access the cell property, like:
cell.name.text = [historyArray objectAtIndex:indexPath.row];
cell.image = [UIImage imageNamed:#"rtb_logo"];
Anyway looks like your UILabel and UIImage is getting the values, but it's not setting them to your cell. To test you can log the value of the UILabel just to check. I don't know if it will work but have you tried this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
cellIdentifier = #"HistoryCell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
UILabel *name = (UILabel*)[cell viewWithTag:40];
name.text =[historyArray objectAtIndex:indexPath.row];
[cell viewWithTag:40] = name; // I don't know if this works
// Check it out if the name.text is getting any value
NSLog(#"UILabel %#",name.text);
UIImageView *image = (UIImageView*)[cell viewWithTag:44];
image.image = [UIImage imageNamed:#"rtb_logo"];
[cell viewWithTag:44] = image; // I don't know if this works
return cell;
}
Normally in iOS, the cells in a tableview are loading on scrolling. In this time I'm setting a label text. In this case I'm setting this text dynamically. When I'm scrolling down it's working as I expected. But when It's scrolling up, it's not working as expected. Here what I tried.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"moduleCell";
DateTableViewCell *cell = (DateTableViewCell*)[tableView dequeueReusableCellWithIdentifier: CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DateTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
cell.backgroundColor = [UIColor clearColor];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#""]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
if (self.newsSegmentControl.selectedSegmentIndex == 1) {
[cell.dateView setHidden:NO];
}else{
[cell.dateView setHidden:YES];
}
NSString *day = [self getDayLabel:date];
if ([dateReturned isEqualToString:day]) {
[cell.lblDate setText:#""];
if (self.newsSegmentControl.selectedSegmentIndex == 1) {
[cell.dateView setHidden:YES];
}
}else{
[cell.lblDate setText:day];
dateReturned = day;
}
}
How may I avoid re-draw the label text in cell on scrolling?
I'm using a PFQueryTableViewController with Parse in my IOS 8 Objective-c iPhone app.
My list consists of a label and a UIImageView where both the label text and image are downloaded from a row in my Parse core. I'm using this code to achieve this:
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:#"Story"];
return query;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [[self objects] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
// Download the header image from parse
PFFile *imageFile = [object objectForKey:#"Image"];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *cellImage = [UIImage imageWithData:imageData];
// Set the cellImage to the cell if it's not nil
if (cellImage == nil) {
// nil - do nothing
NSLog(#"nil");
} else {
NSLog(#"not nil");
// Set the image
UIImageView *cellImageView = (UIImageView *)[cell viewWithTag:40];
cellImageView.image = cellImage;
}
}
}];
// Configure the cell
UILabel *nameLabel = (UILabel*) [cell viewWithTag:10];
nameLabel.text = [object objectForKey:#"Title"];
nameLabel.textColor = [UIColor whiteColor];
// Make the cell transparent
cell.backgroundColor = [UIColor clearColor];
cell.backgroundView = [UIView new];
cell.selectedBackgroundView = [UIView new];
// Resize the cell
[cell sizeToFit];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Hide the tabBar and show the readButton
[self hideTabBar:self.tabBarController];
// Segue over to the viewing page
[self performSegueWithIdentifier:#"detailSegue" sender:self];
// Get the tapped cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *title = ((UILabel*) [cell viewWithTag:10]).text;
// Set selectedStory
MyManager *sharedManager = [MyManager sharedManager];
sharedManager.selectedStory = title;
// Set openedStory to YES as we opened a story
openedStory = YES;
}
This code works good, but the scrolling is a bit laggy, which I think is because it's downloading the image whenever the cell is shown. I thought of created a simple solution by creating an array of images locally and have them only download once, but it has to load 1 time minimum when the app launches. I need to somehow run the download method asynchronously (or another solution that would work).
How can I achieve this?
(I'm using storyboards)
EDIT
Thanks in advance!
Erik
EDIT 2:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (![self.shownIndexes containsObject:indexPath]) {
[self.shownIndexes addObject:indexPath];
UIView *weeeeCell = [cell contentView];
weeeeCell.layer.transform = self.initialTransform;
weeeeCell.layer.opacity = 0.8;
[UIView animateWithDuration:1.25 delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:0.5 options:0 animations:^{
weeeeCell.layer.transform = CATransform3DIdentity;
weeeeCell.layer.opacity = 1;
} completion:^(BOOL finished) {}];
}
}
and
if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) {
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation: UITableViewRowAnimationAutomatic];
}
Your hypothesis about the problem is right, and your idea about a solution is right, too. The additional requirement that you mention about preloading the images is a little fuzzy.
Must they be loaded before the table appears? If they are loaded asynchronously, which they should be, then you'll need to block user's access to the table until the requests are complete. You're replace the poor experience of not seeing the images right away with the worse experience of not seeing the table at all.
I think the better answer is to just load lazily. The outline of the solution is:
Declare a dictionary of images (to be indexed by the indexPaths) and be sure to initialize it to an empty dictionary...
#interface MyViewController () // replace 'MyViewController' with your class
#property(strong,nonatomic) NSMutableDictionary *images;
#end
Use that collection in cellForRowAtIndexPath...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *simpleTableIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
UIImageView *cellImageView = (UIImageView *)[cell viewWithTag:40];
UIImage *cachedImage = self.images[indexPath];
if (cachedImage) {
cellImageView.image = cachedImage;
} else {
cellImageView.image = // put a place holder image here
// load lazily, but read on. the code in the callback should assume
// nothing about the state of the table when it runs
PFFile *imageFile = [object objectForKey:#"Image"];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
// what if this gets run a second time before the first request finishes?
// no worries, check for that here:
if (!error && !self.images[indexPath]) {
UIImage *cellImage = [UIImage imageWithData:imageData];
self.images[indexPath] = cellImage;
// this is important: don't refer to cell in here, it may be
// scrolled away and reused by the time this closure runs
// the code we just wrote to init the cellImageView works just fine
// call that using reload
if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) {
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
}];
}
// Configure the cell
UILabel *nameLabel = (UILabel*) [cell viewWithTag:10];
nameLabel.text = [object objectForKey:#"Title"];
nameLabel.textColor = [UIColor whiteColor];
// Make the cell transparent
cell.backgroundColor = [UIColor clearColor];
cell.backgroundView = [UIView new];
cell.selectedBackgroundView = [UIView new];
// Resize the cell
[cell sizeToFit];
return cell;
}
Edit -- don't bother with this for now, but -- if you really do have the opportunity to prepare the view before its shown (like maybe this view controller is in a tab bar container and not the default tab). You could use the table view helper methods to do a pre-fetch of the visible rows...
- (void)prepareToBeShown {
NSArray indexPaths = [self.tableView indexPathsForVisibleRows];
[self.tableView reloadRowsAtIndexPaths:indexPaths];
}
EDIT 2:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (![self.shownIndexes containsObject:indexPath]) {
[self.shownIndexes addObject:indexPath];
UIView *weeeeCell = [cell contentView];
weeeeCell.layer.transform = self.initialTransform;
weeeeCell.layer.opacity = 0.8;
[UIView animateWithDuration:1.25 delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:0.5 options:0 animations:^{
weeeeCell.layer.transform = CATransform3DIdentity;
weeeeCell.layer.opacity = 1;
} completion:^(BOOL finished) {}];
}
}
Have you thought about using a PFImageView instead of a UIImageView?
All you have to do it set it's file and tell it to load in the background. I've never had any lag when using them in my tableviews.
I have a UITable that reuses cells. Each cell has an image dependant on a condition. The correct images are always used however when cells are reused the old image is displayed behind the new one. I have tried to remove the uiimageview before adding a new subview however it has no effect.
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpTableIden = #"simpTableIden";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpTableIden];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpTableIden];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [[self.achievements objectForKey:[keys objectAtIndex:row]] objectForKey:#"name"];
CGRect picFrame = CGRectMake(cell.frame.size.width-cell.frame.size.height, 0, cell.frame.size.height, cell.frame.size.height);
UIImage *greyDot = [UIImage imageNamed:#"greyDot.png"];
UIImage *whiteDot = [UIImage imageNamed:#"whiteDot.png"];
UIImageView *achievImg = [[UIImageView alloc] init];
tableView.backgroundColor = [UIColor clearColor];
achievImg.frame=picFrame;
if ([[[self.achievements objectForKey:[keys objectAtIndex:row]] objectForKey:#"achieved"] boolValue]) {
achievImg.image = whiteDot;
}
else {
achievImg.image = greyDot;
}
[achievImg removeFromSuperview];
[cell.contentView addSubview:achievImg];
return cell;
}
Any help would be greatly appreciated?
The problem is each time cellForRowAtIndexPath: is called you create a new imageview.
I corrected your code as follows:
Here we create an imageView once for each new new cell and replace it's image every time cellForRowAtIndexPath: is called:
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpTableIden = #"simpTableIden";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpTableIden];
UIImageView *achievImg = nil;
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpTableIden];
//New Cell ... Create Image view
achievImg = [[UIImageView alloc] init];
achievImg.tag = 10;
[cell.contentView addSubview:achievImg];
}
else{
//Old cell...get previously createdd imageview
achievImg = (UIImageView*)[cell.contentView viewWithTag:10];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [[self.achievements objectForKey:[keys objectAtIndex:row]] objectForKey:#"name"];
CGRect picFrame = CGRectMake(cell.frame.size.width-cell.frame.size.height, 0, cell.frame.size.height, cell.frame.size.height);
UIImage *greyDot = [UIImage imageNamed:#"greyDot.png"];
UIImage *whiteDot = [UIImage imageNamed:#"whiteDot.png"];
tableView.backgroundColor = [UIColor clearColor];
achievImg.frame=picFrame;
if ([[[self.achievements objectForKey:[keys objectAtIndex:row]] objectForKey:#"achieved"] boolValue]) {
achievImg.image = whiteDot;
}
else {
achievImg.image = greyDot;
}
//You don't need this because it doesn't have a superview in the first place..it's new
//[achievImg removeFromSuperview];
return cell;
}
try like this,
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpTableIden = #"simpTableIden";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpTableIden];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpTableIden];
NSUInteger row = [indexPath row];
cell.textLabel.text = [[self.achievements objectForKey:[keys objectAtIndex:row]] objectForKey:#"name"];
CGRect picFrame = CGRectMake(cell.frame.size.width-cell.frame.size.height, 0, cell.frame.size.height, cell.frame.size.height);
UIImage *greyDot = [UIImage imageNamed:#"greyDot.png"];
UIImage *whiteDot = [UIImage imageNamed:#"whiteDot.png"];
UIImageView *achievImg = [[UIImageView alloc] init];
tableView.backgroundColor = [UIColor clearColor];
achievImg.frame=picFrame;
if ([[[self.achievements objectForKey:[keys objectAtIndex:row]] objectForKey:#"achieved"] boolValue]) {
achievImg.image = whiteDot;
}
else {
achievImg.image = greyDot;
}
[achievImg removeFromSuperview];
[cell.contentView addSubview:achievImg];
}
return cell;
}
You are always adding the imageView to the contentView of the cell. If the cell is reused that will lead to multiple imageViews in cell.
You can give a tag to the imageView. Always check if there is a view will same tag in the contentView of cell. If not add the imageView.
UIImageView *achievImg = (UIImageView *)[cell.contentView viewWithTag:55];
if(!achievImg){
achievImg = [[UIImageView alloc] init];
achievImg.tag = 55;
[cell.contentView addSubView:achievImg];
}
In cellForRowAtIndexPath I am adding a UIImageView to cell.contentView. The problem is that when the cell scrolls off the screen and back on, it adds the same image again on top of the one that is already there. This happens continuously until I get a very stacked-up blurry image.
Do you have to keep removing any image views that you add to cell.contentView? If so, in what delegate method do you do that in?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
MyTableCell *cell = (MyTableCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.jpg"]];
imageView.center = CGPointMake(310, 48);
[cell.contentView addSubview:imageView];
[imageView release];
return cell;
}
If you don't want to keep putting imageViews in your cell, you have to do all the customization inside the if(cell==nil) block, otherwise it will add one every time a cell recycles. When using cell recycling you always want to keep anything that is consistent for all of your cells in that block so that they only get added once.
Ex:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
MyTableCell *cell = (MyTableCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
//Add custom objects to the cell in here!
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.jpg"]];
imageView.center = CGPointMake(310, 48);
[cell.contentView addSubview:imageView];
[imageView release];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
If each cell should need a different image, you can try something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
static const int ImageViewTag = 1234; //any integer constant
MyTableCell *cell = (MyTableCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UIImageView *imageView;
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
//Add custom objects to the cell in here!
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0, imgWidth, imgHeight)];
imageView.center = CGPointMake(310, 48);
imageView.tag = ImageViewTag;
[cell.contentView addSubview:imageView];
[imageView release];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else
{
imageView = [cell viewWithTag:ImageViewTag];
}
imageView.image = yourUIImageForThisCell;
return cell;
}