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.
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;
}
Background
I have a UIViewController which handles 2 UITableViews, both with custom UITableViewCell subclasses. A click event on the top table (Categories) is supposed to trigger a reloadData on the bottom table (Article List from an RSS feed) depending on which category is selected. What is supposed to happen is that the new data gets pulled and the relevant array is repopulated, after which the data gets displayed on the bottom table.
The data that is meant to be displayed is:
An image
a UILabel (for the date)
A UITextView for the title
1) First problem
The list that loads by default upon starting the app loads properly (well almost but I'l get to the 'almost' in #2) but once a category is selected in the top table, the array containing the data to be displayed in the cells is rebuilt with the relevant data but the reloadData method does not immediately invoke the desired results. Only once scrolling downwards and then upwards does the new data show. Using debugging I can see that the data is being loaded correctly into the array, so I'm sure its a UITableViewController or UITableViewCell issue.
I have tried various solutions discussed here on StackOverflow, other than the obvious self.myTableView.reloadData the two most common being invoking ReloadData as shown below:
[self.myTableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
and also
dispatch_async(dispatch_get_main_queue(), ^{
[self.myTableView reloadData];});
}
Each time I've attempted to call these from within the ArticlesTableViewController instance, to no success.
2) Second problem
DateLabel only shows on the first cell upon opening the app, and then for the dateLabel to show in the rest of the cells I actually have to scroll downwards, and then up again. Cells coming back into view from above then contain the dateLabel, but if a cell appears back into view from below then its gone again. Pretty confusing stuff.
Here is my relevant code
cellForRowAtIndexPath (in the ArticlesTableViewController):
// Method that gets fired when parsing is complete via NSNotification
- (void) parsingComplete {
//Tell the tableview to animate the changes automatically
self.articleList = [[NSMutableArray alloc]initWithArray:parser.articles];
[myTableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
ArticlesTableViewCell *cell = (ArticlesTableViewCell *)[myTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell){
cell = [[ArticlesTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
dispatch_async(kBgQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[self.articleList valueForKey:#"enclosure"] objectAtIndex:indexPath.row]]];
if (imgData) {
UIImage *image = [UIImage imageWithData:imgData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.enclosure.image = image;
[cell.enclosure setNeedsDisplay];
});
}
}
});
[cell.dateLabel setText:[[self.articleList valueForKey:#"pubDate"] objectAtIndex:indexPath.row]];
[cell.headingTextView setText:[[self.articleList valueForKey:#"title"] objectAtIndex:indexPath.row]];
cell.headingTextView.editable = NO;
return cell;
}
CustomCell code (for ArticlesTableViewCell):
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.enclosure = [[UIImageView alloc] initWithFrame:CGRectMake(10,10,48,48)];
self.enclosure.tag = 1;
//self.imageView = nil;
self.dateLabel = [[UILabel alloc] initWithFrame: CGRectMake (75,-10,50,50)];
self.dateLabel.textColor = [UIColor grayColor];
self.dateLabel.font = [UIFont fontWithName:#"Arial" size:8.0f];
self.headingTextView= [[UITextView alloc] initWithFrame:CGRectMake(70, 20, 400, 80)];
self.headingTextView.textColor = [UIColor blackColor];
self.headingTextView.font = [UIFont fontWithName:#"Arial" size:10.0f];
[self addSubview:self.dateLabel];
[self addSubview:self.enclosure];
[self addSubview:self.headingTextView];
//Here the Date only appears in the first cell, but when I scroll down and up again it re-appears
}
return self;
}
EDIT
Below is the CellForRowAtIndexPath code in the CategoriesTableViewController (the first table):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
CatTableViewCell *cell = (CatTableViewCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CatTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.nameLabel.text = [categories objectAtIndex:indexPath.row];
return cell;
}
And here is the code in the ViewController that instantiates these two tableViewControllers:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *categoryView = [[UIView alloc] init];
[categoryView setFrame:CGRectMake(60,0, 100,-200)];
UIView *articlesView = [[UIView alloc] init];
[articlesView setFrame:CGRectMake(0,50, 400,400)];
CatBarTVC *categoryBar = [[CatBarTVC alloc] initWithStyle:UITableViewStylePlain];
categoryBar.view.transform = CGAffineTransformMakeRotation(-M_PI * 0.5);
categoryBar.view.autoresizesSubviews=NO;
ArticlesTVC *articles = [[ArticlesTVC alloc] initWithStyle:UITableViewStylePlain];
[articlesView addSubview:articles.view];
//[self addChildViewController:articles];
//[articlesView addSubview:articles.view];
[self.view addSubview:categoryBar.view];
[self.view addSubview:articles.view];
[self addChildViewController:categoryBar];
[self addChildViewController:articles];
categoryBar.view.frame =CGRectMake(0,0, 500,70);
articles.view.frame =CGRectMake(0,50, 400,400);
}
I see numerous problems In the code. Please look at the comments in the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Suggestion: Try to keep identifier in ArticlesTableViewCell if you want to use it in more than one table view controller. Don't use _tableView. _tableView is iVar. You should use it only in getters, setters and in init methods.
static NSString *CellIdentifier = #"Cell";
// Don't call "self.myTableView" of one specific tableView but for current one that is apassed by argument (tableView).
ArticlesTableViewCell *cell = (ArticlesTableViewCell *)[self.myTableView dequeueReusableCellWithIdentifier:CellIdentifier];
// You override here existing cell. Try to use if(cell==nil) before creating new cell.
cell = [[ArticlesTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
dispatch_async(kBgQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[self.articleList valueForKey:#"enclosure"] objectAtIndex:indexPath.row]]];
if (imgData) {
UIImage *image = [UIImage imageWithData:imgData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
// You should call [cell setNeedsDisplay to inform app that this view have to be redrawn.
cell.enclosure.image = image;
});
}
}
});
// No need to call this on global quele. You should call this directly (e.g. cell.dataLabel = self.articleList[#"pubDate"][indexpath.row];)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cell.dateLabel performSelectorOnMainThread:#selector(setText:) withObject:[[self.articleList valueForKey:#"pubDate"] objectAtIndex:indexPath.row] waitUntilDone:NO modes:#[NSRunLoopCommonModes]];
[cell.headingTextView performSelectorOnMainThread:#selector(setText:) withObject:[[self.articleList valueForKey:#"title"] objectAtIndex:indexPath.row] waitUntilDone:NO modes:#[NSRunLoopCommonModes]];
cell.headingTextView.editable = NO;
});
return cell;
}
I've been trying to figuered this out for hours. I'm just plain old stuck here. What im trying to accomplish is basically inserting a row directly below the row just tapped in the tableview in addition i would like to add and image to the row and and make the image clickable to respond to its click event.
So here is my code.
I implemented (i belive) the nessesary methods to handle all the actions for the uitableview.
when the user taps the cell i handle that action by executing the following code.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (debug==1) {
NSLog(#"running line 225%# '%#'", self.class, NSStringFromSelector(_cmd));
}
Locations *location = nil;
Locations *tempObject = [[Locations alloc]init];
//test to see if we are looking for the search box or if we are essentially looking from the main view controller.
if (self.searchDisplayController.active) {
location= [self.searchResults objectAtIndex:indexPath.row];
NSLog(#"location : %#" ,location.locationName);
} else {
location = [self.locations objectAtIndex:indexPath.row];
NSLog(#"location : %#" ,location.locationName);
//set the new indexpath to 1 more then before so that we can essetially add a row right below the actual tapped item.
NSIndexPath *newPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section];
indexPath = newPath;
[self.locations insertObject:tempObject atIndex:indexPath.row ];
[tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
self.visibleCell =YES; //set this boolean variable so that we can add a specific row image to this var
// self.locations[0].isItVisible = YES;
}//ends the else statement.
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
the above code inserts an empty cell into my tableview.
however how can i set the cell so that its custom and not the same as the others. In other words my initial cells data-source are basically bound to an nsobject and a string property location-name. However when i go try to update the table cells in the above method i obviously cannot add an image into a string so I'm running in to a error.
so i tried to instead make the update on the
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
by basically checking if a variable is set to true or false but that turned out to be buggy because even when i scroll this method gets called.
How should i go about doing this. I think i have to do it all in the didselectrowindexaspath method. But i cant figured out how to change the newly inserted cell to contain an image only.
Any help would be appreciated.
EDIT
here is what im doing to try to add the image under the cellforrowindexpath method.
if(self.visibleCell==YES){
UIImage *clkImg = [UIImage imageNamed:#"alarm_clock.png"];
cell.imageView.image = clkImg;
}
Im a noob so im not sure im doing this correctly.
EDIT
this is the full cellforatindexpath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (debug==1) {
NSLog(#"running line 159 %# '%#'", self.class, NSStringFromSelector(_cmd));
}
// NSLog(#"cell for row at index path just got called");
//JAMcustomCell *myCell = [[JAMcustomCell alloc]init];
static NSString *CellIdentifier = #"ListPrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Locations * locations = [[Locations alloc]init];
//tableView.backgroundColor = [UIColor blackColor];
// NSLog(#"this is visible '%hhd'", locations.isItVisible);
if(self.visibleCell==YES){
UIImage *clkImg = [UIImage imageNamed:#"alarm_clock.png"];
cell.imageView.image = clkImg;
}
if (tableView == self.searchDisplayController.searchResultsTableView)
{
locations = [self.searchResults objectAtIndex:indexPath.row];
}
else{
locations = [self.locations objectAtIndex:indexPath.row];
}
cell.textLabel.text = locations.locationName;
cell.textLabel.textColor = [UIColor whiteColor];
//cell.backgroundColor =[UIColor blackColor];
// cell.backgroundColor =[UIColor colorWithPatternImage:[UIImage imageNamed:#"Graytbl.fw.png"]];
cell.backgroundView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"blueTbl.fw.png"]];
cell.selectedBackgroundView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"blueTbl.fw.png"]];
// UIFont *myFont = [ UIFont fontWithName: #"Oswald" size: 25.0 ];
// cell.textLabel.font = myFont;
cell.textLabel.font= self.MyFont;//[UIFont fontWithName:#"Oswald-Regular.ttf" size:15];
return cell;
}
Try this approach, I used your idea of Bool
#pragma mark - Table View Data Source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.numberOfRows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(self.visibleCell){
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"imageViewCell" forIndexPath:indexPath];//ListPrototypeCell
UIImageView *imageVIew = (UIImageView *)[cell viewWithTag:1];
[imageVIew setImage:[UIImage imageNamed:#"alarm_clock.png"]];
return cell;
}else{
return [tableView dequeueReusableCellWithIdentifier:#"ListPrototypeCell" forIndexPath:indexPath];
}
}
#pragma mark - Table View Delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if(!self.visibleCell){
self.numberOfRows++;
self.visibleCell = YES;
NSIndexPath *indexPathCell = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPathCell] withRowAnimation:UITableViewRowAnimationBottom];
}else{
self.numberOfRows--;
self.visibleCell = NO;
NSIndexPath *indexPathCell = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:0];
[self.tableView deleteRowsAtIndexPaths:#[indexPathCell] withRowAnimation:UITableViewRowAnimationTop];
}
}
I created a demo project for you.
I hope it helps
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I want to know how to set image in Selected cell row in UITableView.
If I select the first cell, image should display in first cell and later when I select the second cell, image should display in second cell only (and not in first cell)
Basically, image should display in the selected cell only.
The cleanest approach would be to subclass UITableViewCell.
#import <UIKit/UIKit.h>
#interface SongCell : UITableViewCell
#property (nonatomic, assign) BOOL isPlaying;
#end
#interface SongCell ()
#property (nonatomic, strong) UIImage *speakerImage;
#end
#implementation SongCell
-(void)layoutSubviews
{
if (!_speakerImage) {
// http://commons.wikimedia.org/wiki/File:Speaker_Icon.svg
self.speakerImage = [UIImage imageNamed:#"speaker_icon.png"];
}
if(!self.isPlaying){
self.imageView.image = nil;
} else {
self.imageView.image = _speakerImage;
self.imageView.hidden =NO;
}
[super layoutSubviews];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if (!selected) {
self.isPlaying = NO;
}
}
-(void)setIsPlaying:(BOOL)isPlaying
{
_isPlaying = isPlaying;
[self setNeedsLayout];
}
#end
The UITableViews datasource and delegate implementation
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 20;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"SongCell";
SongCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SongCell *cell =(SongCell *)[tableView cellForRowAtIndexPath:indexPath] ;
cell.isPlaying = !cell.isPlaying;
}
I prepared a demo project for you. You will find it on GitHub
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
[UIView animateWithDuration:2.0f animations:^{
[imageView setHidden:NO];
} completion:^(BOOL finished) {
;
}];
}
You want a toggling type nature for the imageView.
- (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];
}
[cell.imageView setImage: [UIImage imageNamed:#""]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//refresh all cells
//basically call cellForRowAtIndexPath to reset all cells
[tableView reloadSections:indexPath.section
withRowAnimation:UITableViewRowAnimationFade];
//get cell for the currently selected row
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//set image
[cell.imageView setImage: [UIImage imageNamed:#"loudspeaker.png"]];
}
But this is just a visual aspect.
If you select the first row and scroll down and later return to the first row, the image would no longer be there because -cellForRowAtIndexPath: is recalled and since we had [cell.imageView setImage: [UIImage imageNamed:#""]]; in it, the imageView.image is reset.
To handle the above scenario, you need to remember which rows were selected. (you can achieve this by a basic array)
Example:
- (void)viewDidLoad {
[super viewDidLoad];
//arrMyDataSource is defined in .h as 'NSArray *arrMyDataSource;'
//needless to say but this is the tableView's datasource contents
arrMyDataSource = #[#"Apple",#"Banana",#"Candy",#"Door",#"Elephant"];
//arrMemoryMap is defined in .h as 'NSMutableArray *arrMemoryMap;'
//this is one way to remember which row is selected
arrMemoryMap = [NSMutableArray alloc] init];
for (int i = 0 ; i < arrMyDataSource.count ; i++) {
NSNumber *tmpSelectStatus = [NSNumber numberWithBool:NO];
[arrMemoryMap addObject: tmpSelectStatus];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//...
if([[arrMemoryMap objectAtIndex:indexPath.row] boolValue] == YES) {
[cell.imageView setImage: [UIImage imageNamed:#"loudspeaker.png"]];
} else {
[cell.imageView setImage: [UIImage imageNamed:#""]];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//first, reset arrMemoryMap for all rows
for (int i = 0 ; i < arrMemoryMap.count ; i++) {
if([[arrMemoryMap objectAtIndex:i] boolValue] == YES]) {
[[arrMemoryMap objectAtIndex:i] setBoolValue: NO];
break;
}
}
//now, set current row as selected in arrMemoryMap
[[arrMemoryMap objectAtIndex:indexPath.row] setBoolValue: YES];
//reload data or particular section
[tableView reloadSections:indexPath.section
withRowAnimation:UITableViewRowAnimationFade];
}
For work purposes I need to create a UIScrollView which embeds a UIView which in his turn embeds an UITableView via the container feature in Xcode.
My UIScrollView is a full page scrollview with Paging enabled.
My UIView is filled with a UIImage, some UIButton's and a container linking to a UITableView.
On initial launch, the data is loaded perfectly, meaning the UITableView is filled with the data, the UIImage is filled, and the Buttons are placed correctly.
But for some strange reason the when I try to tap or scroll in the UITableView in the container all the data from my UITableView gets cleared.
I'm posting this question here, as I have not found any other similar issue on StackOverFlow or any other website.
UITableViewCode:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.productTable setBackgroundView:nil];
self.productTable.backgroundColor = [UIColor clearColor];
self.productTable.delegate = self;
self.productTable.dataSource = self;
}
- (void) viewDidAppear:(BOOL)animated {
/*CGSize tmp = self.productTable.contentSize;
self.productTable.frame = CGRectMake(0, 0, tmp.width, tmp.height * 3);*/
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
NSLog(#"section count : %i", [self.Products count]);
return [self.Products count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
xcsSectionInfo *sectionInfo = [self.Products objectAtIndex:section];
if (sectionInfo.isOpen == NO) {
return 1;
} else {
return 3;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
xcsSectionInfo *sectionInfo = [self.Products objectAtIndex:indexPath.section];
if (indexPath.row == 0) {
static NSString *CellIdentifier = #"Header";
xcsProductHeaderCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.articleNumber.text = sectionInfo.product.articleNumber;
cell.articleColor.text = sectionInfo.product.articleColor;
cell.backgroundColor = [UIColor grayColor];
if (sectionInfo.isOpen == YES && sectionInfo == self.currentSectionInfo) {
cell.expandImage.image = [UIImage imageNamed:#"arrow_down.png"];
} else if (sectionInfo.isOpen == NO) {
cell.expandImage.image = [UIImage imageNamed:#"arrow_up.png"];
}
return cell;
} else if (indexPath.row == 1) {
static NSString *CellIdentifier = #"ProductHeader";
xcsProductTitleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.colorTempHeader.text = #"Color Temperature";
cell.sourceQualityHeader.text = #"Source Quality";
cell.sourceTypeHeader.text = #"Source Type";
cell.luminaireFluxHeader.text = #"Luminaire Flux";
cell.powerConsumptionHeader.text = #"Power Consumption";
cell.luminaireEfficacyHeader.text = #"Luminaire Efficacy";
cell.backgroundColor = [UIColor grayColor];
return cell;
} else if (indexPath.row == 2) {
static NSString *CellIdentifier = #"Product";
xcsProductCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.colorTemp.text = sectionInfo.product.colorTemperature;
cell.sourceQuality.text = sectionInfo.product.sourceQuality;
cell.sourceType.text = sectionInfo.product.sourceType;
cell.luminaireFlux.text = sectionInfo.product.luminaireFlux;
cell.powerConsumption.text = sectionInfo.product.powerConsumption;
cell.luminaireEfficacy.text = sectionInfo.product.luminaireEfficacy;
cell.backgroundColor = [UIColor grayColor];
return cell;
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
xcsSectionInfo *sectionInfo = [self.Products objectAtIndex:indexPath.section];
NSIndexPath *path0 = [NSIndexPath indexPathForRow:[indexPath row]+1 inSection:[indexPath section]];
NSIndexPath *path1 = [NSIndexPath indexPathForRow:[indexPath row]+2 inSection:[indexPath section]];
NSArray *indexPathArray = [NSArray arrayWithObjects: path0, path1, nil];
if (sectionInfo.isOpen == NO) {
sectionInfo.isOpen = YES;
[tableView insertRowsAtIndexPaths:indexPathArray withRowAnimation:NO];
} else {
sectionInfo.isOpen = NO;
[tableView deleteRowsAtIndexPaths:indexPathArray withRowAnimation:NO];
}
[self.Products replaceObjectAtIndex:indexPath.section withObject:sectionInfo];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
self.currentSectionInfo = sectionInfo;
[tableView reloadData];
}
Btw.: I'm using storyboards
Regards and thanks in advance.
UPDATE 2:
I think a UIPageViewController would be more appropriate (link). It looks like it accomplishes what you are trying to achieve. And probably much more simple than managing scroll views embedded in other scroll views.
UPDATE:
It looks like what you are trying to achieve is made possible in the UIPageViewController (link). If this works, it would be better than trying to manage scroll views embedded in other views.
Embedding a UITableView is specifically NOT recommended by Apple. Conflicts arise when the system is trying to figure out where to send events:
Important: You should not embed UIWebView or UITableView objects in
UIScrollView objects. If you do so, unexpected behavior can result
because touch events for the two objects can be mixed up and wrongly
handled.
(source)
But here is the stupid part, when you go to the source link, you will notice that appears in the docs for the UIWebView. Apple forgot to include it in the docs for UITableView.