I am new to iOS and i have some serious problem i don't know why this happened, i also tried to find out the reason behind this but i failed.
I am creating a app in iPhone which take tweet of particular user using twitter api, i have no problem in getting the json data, also able to display it in UITableView in iOS 6 using storyboard, i had changed the row height to 200 px, so that i can display user name text tweet and image.
But when I scroll up or down the UITableViewCell the scrollbar goes out of the screen.
What may be the reason?how can I fix it?
ScreenShots
First it shows like this when the app loads the scrollbar takes the full screen
then when i scroll it get out of the screen
at last it completely gets out of screen
UITableView delegate methods
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *name,*time,*tweet,*image;
name=[[NSMutableArray alloc]init];
time=[[NSMutableArray alloc]init];
tweet=[[NSMutableArray alloc]init];
image=[[NSMutableArray alloc]init];
static NSString *CellIdentifier = #"SimpleTableItem";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
for (User *userModel in self.user) {
[name addObject:userModel.nameStr];
[image addObject:userModel.imageStr];
}
for (Json *jsonModel in self.json) {
[tweet addObject:jsonModel.tweetStr];
[time addObject:jsonModel.timeStr];
}
// Fetch using GCD
dispatch_queue_t downloadThumbnailQueue = dispatch_queue_create("Get Photo Thumbnail", NULL);
dispatch_async(downloadThumbnailQueue, ^{
NSString *imageURL=[image objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:imageURL];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[UIImage alloc] initWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
UIImageView *image = (UIImageView *)[cell.contentView viewWithTag:5];
image.image=img;
[cell setNeedsLayout];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[spinner stopAnimating];
});
});
self.labelName = (UILabel *)[cell.contentView viewWithTag:10];
[self.labelName setText:[NSString stringWithFormat:#"%#", [name objectAtIndex:indexPath.row]]];
self.labelTime = (UILabel *)[cell.contentView viewWithTag:11];
[self.labelTime setText:[NSString stringWithFormat:#"%#", [time objectAtIndex:indexPath.row]]];
self.labelTweet = (UILabel *)[cell.contentView viewWithTag:12];
[self.labelTweet setText:[NSString stringWithFormat:#"%#", [tweet objectAtIndex:indexPath.row]]];
cell.imageView.frame=CGRectMake(0, 0, 40, 40);
cell.textLabel.lineBreakMode=NSLineBreakByWordWrapping;
return cell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat rowHeight=self.labelName.frame.size.height+self.labelTime.frame.size.height+self.labelTweet.frame.size.height+50;
return rowHeight;
}
Well scroll goes out of the screen means the tableView frame is larger than the screen size
Set the frame of tableview properly using setFrame: method
I think you have to check the height of the UITableView. Show the screenshot.
Here the ScrollBar goes down as the Height of TableView is larger then the UIView in which you are adding TableView. Adjust the frame according to your view then the ScrollBar will not disappear.
Related
I've an UIImageView in customized UITableViewCell and I want to resize it, I set it view mode to LEFT. To resize I'm applying below code.
[cell.IBImage setFrame:CGRectMake(201,12,50,20)];
My image width is 100 and I want to reduce is to 50 but it still shows me in 100 width. If I set view mode to Scale To Fill it works fine but I want to have it's view mode to LEFT.
Full method cellForRowAtIndexPath is as given below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"DetailCell";
DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"DetailCell" owner:self options:nil];
cell = (DetailCell *)[topLevelObjects objectAtIndex:0];
clsDetailResponse *Response = [self.Histories objectAtIndex:[indexPath row]];
[cell.IBUsernameLabel setText:[NSString stringWithFormat:#"%# %#", Response.Firstname, Response.Lastname]];
[cell.IBImage setFrame:CGRectMake(201, 12, 50, 20)];
return cell;
}
You are doing to resize the imageView frame not the image so You can crop your image as you want,
UIImage *ima = cell.image.image;
CGRect cropRect = CGRectMake(0, 0, 50,20);
CGImageRef imageRef = CGImageCreateWithImageInRect([ima CGImage], cropRect);
[cell.image setImage:[UIImage imageWithCGImage:imageRef]];
CGImageRelease(imageRef);
cell.image.frame=CGRectMake(10, 10, 50, 20);
set IBImage frame in DetailCell class in
-(void)layoutSubviews:
method
Add the following lines to the File's owner to make sure it knows how to respond to framechanges.
[self setAutoresizingMask:UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth];
[self setClipsToBounds:YES];
[self setAutoresizesSubviews:YES];
I'm loading asynchronously images into UITableView and each row has different image. the problem is when i scroll the UITableView the previous images are loaded again in the new rows and after a while these images are changed to the correct ones (this problem is gone when i removed dequeueReusableCellWithIdentifier but it reduces the performance)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCellFriends" owner:self options:nil];
cell = [nibObjects objectAtIndex:0];
nibObjects = nil;
}
UIImageView *avatar = (UIImageView *)[cell viewWithTag:100];
UILabel *name = (UILabel *)[cell viewWithTag:2];
UILabel *city = (UILabel *)[cell viewWithTag:3];
EntryProfile *entryForRow = [entriesProfileArray objectAtIndex:indexPath.row];
avatar.image = nil;
NSString *strLinkImage = [entryForRow.apiAvatarPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSArray *args = [NSArray arrayWithObjects:avatar,strLinkImage,[NSString stringWithFormat:#"%d",indexPath.row], nil];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(addPhotoWithImageView:) object:args];
[operationQueue addOperation:operation];
[operation release];
// Set the name of the user
name.text = entryForRow.name;
return cell;
}
// Method
- (void)addPhotoWithImageView:(NSArray *)args
{
UIImageView *imageView = (UIImageView *)[args objectAtIndex:0];
NSString *strLinkImage = (NSString *)[args objectAtIndex:1];
NSString *index = (NSString *)[args objectAtIndex:2];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.example.com%#",strLinkImage]]];
UIImage *imageField = [[UIImage alloc] initWithData:imageData];
if (imageData) {
[imageView performSelectorOnMainThread:#selector(setImage:) withObject:imageField waitUntilDone:NO];
}
[imageField release];
}
If there are people still facing this issue, I see the answers above do not really help.
2 steps to take:
You need to "cancel" the asynchronous request that is ongoing for the image view and then set a new image asynchronously with a placeholder image till the async request goes through
Ideally, do not send your async request while the user is scrolling through
(tableview/scrollview is decelerating) - try to avoid making new requests and just set the placeholder in such cases (unless you have a way of confidently knowing that the request was done before and hence it is likely that the image is found in the local disk cache for the image url)
The above 2 steps will keep your functionality as intended and give you good performance too.
Try to implement your own UItableViewCell and set the all your cell properties to nil in the PrepareForReuse method.
I would also recomend you to take a look at this project in order to load asynchronously remote images:
https://github.com/nicklockwood/AsyncImageView
I used it with great success in different projects.
in cellForRowAtIndexPath,
before you call:
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(addPhotoWithImageView:) object:args];
force yourCell imageView.image to nil
imageView.image = nil;
this way you clean the contents image of old cell, then it will be redraw with right image when it's loaded...
EDIT
maybe nil doesn't works...
try a placeHolder image, a real png file (with a color, for test, then it may be a transparent file...)
Had a similar issue the other day and the way I solved it was the following ( I didn't notice any performance reduction ).
UITableViewCell *cell;
if (cell == nil) {
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// rest of the code
}
So basically invoke the dequeue inside the if.
This is a classic problem, easily solved.
In your cellForRowAtIndexPath: implementation, you can include code like this:
if (!tableView.decellerating) {
// set avatar.image to its correct UIImage value how ever you like,
// in the background if necessary
} else {
// table is scrolling...
avatar.image = nil;
}
And then implement this method in your view controller:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
NSArray *visibleCells = [self.tableView visibleCells];
[visibleCells enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UITableViewCell *cell = (UITableViewCell *)obj;
UIImageView *avatar = (UIImageView *)[cell viewWithTag:100];
avatar.image = ...; // Do what you need to do to set the image in the cell
}];
}
You will also need to add UIScrollViewDelegate to the list of protocols your view controller adopts.
The idea here is that as long as the table view is moving, don't bother loading the images. Your performance will skyrocket.
I am trying to load locally stored images in a background thread and set them as UITableViewCell images. I keep getting an exception and don't really know how to approach it.
Here is my code:
- (void)loadImageInBackground:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *path = [[tableImages objectAtIndex:indexPath.section]objectAtIndex:indexPath.row];
UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:path ofType:#"png"]];
[self performSelectorOnMainThread:#selector(assignImageToImageView:) withObject:img waitUntilDone:YES];
[img release];
[pool release];
}
- (void) assignImageToImageView:(UIImage *)img
{
UITableViewCell *cell = (UITableViewCell *)[self.tableView viewWithTag:0];
((UIImageView *)cell.imageView).image = img;
}
Anyone have any ideas? Thanks to all!
As is mention in your error you are trying to set image to object of class UITableView. It is impossible.
In your code it is error here:
UITableViewCell *cell = (UITableViewCell *)[self.tableView viewWithTag:0];
By default all views has default view tag set to 0. When your are calling that method to your tableView it returns you first UIView with tag == 0. I think you didn't change default tag of your UITableView *tableView so that method returns you tableView and not UITableViewCell.
In this case, you need to choose right way to access appropriate UITableViewCell *cell;.
Greetings,
I'm trying to implement a tableview with asynchronously loading images (80px x 60px). I have the async bit working, however my text (cell.textlabel.text) isn't showing up correctly - it overlaps the image!
Here is a photo of my iPhone:
Any ideas about how I might fix this?
I've tried setting a frame with CGRectMake, but it's not working... All out of ideas here and thought I'd ask the stackoverflow community.
Many thanks in advance,
Here is the code I am using:
-(UITableViewCell ) tableView:(UITableView)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{
static NSString *CellIdentifier = #"ImageCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithFrame:CGRectZero reuseIdentifier:CellIdentifier]
autorelease];
} else {
AsyncImageView* oldImage = (AsyncImageView*)
[cell.contentView viewWithTag:999];
[oldImage removeFromSuperview];
}
CGRect frame;
frame.size.width=80; frame.size.height=60;
frame.origin.x=0; frame.origin.y=0;
AsyncImageView* asyncImage = [[[AsyncImageView alloc]
initWithFrame:frame] autorelease];
asyncImage.tag = 999;
NSString *urlStr = [[NSString alloc] initWithFormat:#"http://www.example.com/driverPhotos/%#/%#_80.png",[options objectAtIndex:[indexPath row]],[images objectAtIndex:[indexPath row]]];
NSURL* url=[NSURL URLWithString:urlStr];
[asyncImage loadImageFromURL:url];
[cell.contentView addSubview:asyncImage];
cell.textLabel.frame=CGRectMake(100, 0, 220, 60);
cell.textLabel.text=#"Hi";
return cell;
}
You can't change the frame's of any of the default subviews (detailTextLabel, textLabel, imageView, etc) If you really want to then you'll have to subclass UITableView and override Layout Subviews..
I'd suggest ignoring cell.textLabel and creating your own UILabel and adding it to cell.contentView in the same way you are adding your AsyncImageView (with tags, etc).
If you don't set anything on cell.textLabel then it won't appear and cover up your AsyncImageView. (however if it does then just bring you AsyncImageView to the front)
Btw, if you press the Home button and the lock button at the same time, the iPhone will take a screenshot for you and save it into the photoalbum. Where you can email it to yourself and get it on your PC.
Or you can have a custom cell and implement x, y in layoutSubviews:
-(void) layoutSubviews
{
[super layoutSubviews];
[[self textLabel] setFrame:CGRectMake(75,10,250,20)];
[[self detailTextLabel] setFrame:CGRectMake(75,30,250,20)];
}
hi i am working with Twitter, i got problem with images, i have tweet view have UITableview, it contains all tweet with user photo, if i load those photos in each cell when i scroll the UITableview, it scrolling very very slowly please suggest me to reduce the memory size of photos and scroll the UITableView fast.
i heard the thumbnail can reduce the memory size, does it.(if not which method i have to choose and what thumbnail method do's) if so how to do that in this code (table view cell code)
//Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if(!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStyleGrouped reuseIdentifier:nil] autorelease];
UIImageView *myImage = [[UIImageView alloc]initWithFrame:CGRectMake(6,10,58,60)] ;
NSURL *url = [NSURL URLWithString:[(Tweet*)[tweetArray objectAtIndex:indexPath.row] image_url]]; //here Tweet is other class and image_url is method in the Tweet class
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
[myImage setImage: [UIImage imageWithData:data]];
[cell.contentView addSubview:myImage];
[myImage release];
}
return cell;
}
Thank you,
please suggest me
The reason for slow scroll of the table view is that you are trying to get the image data for every cell on the main thread. UI is getting blocked while it is going to fetch the image data from the URL and, According to me its not good to download images on main thread while table view is also loaded.
Instead of using this approach, You should have to use NSOperationQueue, NSOperation and NSThread for async load of images to the appropriate cell.
If you need more help or simple code like 2-3 function to download images async...
Here are the functions....
Where you are parsing/getting the values only call [self startLoading]; It will load images without blocking UI.
- (void) startLoading {
NSOperationQueue *queue = [[[NSOperationQueue alloc]init]autorelease];
NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(loadImagesInBackground) object:nil];
[queue addOperation:op];
[op release];
}
-(void) loadImagesInBackground {
int index = 0;
for (NSString urlString in [(Tweet*)[tweetArray objectAtIndex:indexPath.row] image_url]) {
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
[myImageArray addObject: [UIImage imageWithData:data]];
index++;
if(index/3==0)
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if(!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStyleGrouped reuseIdentifier:nil] autorelease];
UIImageView *myImage = [[UIImageView alloc]initWithFrame:CGRectMake(6,10,58,60)] ;
[myImage setImage: [myImageArray objectAtIndex:indexpath.row]];
[cell.contentView addSubview:myImage];
[myImage release];
}
return cell;
}
This article (Download images for a table without threads) might have your answer.
There's also this sample in the iOS dev library, which shows how to load images assynchronously, as quickly as possible.
For sure you want to download your images lazily and asynchronously as described by other answerers.
You also probably want to resize it to your thumbnail size and store it in memory at the smaller size. See this article for tutorial and actual usable library code to do that (scroll down past the rant about copy-and-paste development).
EDIT: You clearly need more help with the background download part. Here's how I do it.
Install ASIHTTPRequest, which is a third party library that GREATLY simplifies HTTP client work. Follow the instructions to install it in your project, and make sure you put #include "ASIHTTPRequest.h" at the top of your .m file.
Then in your tableView: cellForRowAtIndexPath:, go:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if(!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
UIImageView *myImage = [[UIImageView alloc]initWithFrame:CGRectMake(6,10,58,60)] ;
[cell.contentView addSubview:myImage];
[myImage release];
}
// so at this point you've got a configured cell, either created fresh
// or dequeued from the table's cache.
// what you DON'T have is a pointer to the uiimageview object. So we need to
// go get it.
UIImageView *theImageView;
for (UIView *view in cell.contentView.subviews) {
if ([view isKindOfClass:[UIImageView class]) {
theImageView = view;
break;
}
}
NSURL *url = [NSURL URLWithString:[(Tweet*)[tweetArray objectAtIndex:indexPath.row] image_url]]; //here Tweet is other class and image_url is method in the Tweet class
ASIHTTPRequest *req = [ASIHTTPRequest requestWithURL:url];
[req setCompletionBlock:^{
NSData *data = [req responseData];
// This is the UIImageView we extracted above:
[theImageView setImage: [UIImage imageWithData:data]];
}];
// this will start the request in the background, and call the above block when done.
[req startAsynchronous];
}
// Then return the cell right now, for the UITableView to render.
// The image will get filled in later when it returns from the server.
return cell;
}