SetNeedsDisplay in table view - objective-c

My problem is that not all cell are correct initialized with the correct UIView as an rendered image.( see code below) On a iphone 4 it always the same cel on a iphone with retina display is also an other cell. In this set-up setNeedsDisplay won't function.
If i use the same structure in an IBoutlet it's working.
!
I need to use in some cells the image-files .png and at some other cells the defined drawing method, which uses the drawrect or better i should use the setNeedsDisplay method.....
What is going wrong!!!
my code
tableview... cell
static NSString *CellIdentifier = #"TypeCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
CellTypes *cellType = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = cellType.title;
cell.detailTextLabel.text = cellType.detail;
if ( [cellType.type rangeOfString:#"TL"].location != NSNotFound) {
cell.imageView.image = [UIImage imageNamed:[cellType.type stringByAppendingString:#"Thumb"]];
cell.indentationWidth = 10;
}
else {
static CGFloat scale = 0.0; // old API , screen values
UIScreen *screen = [UIScreen mainScreen];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 4.0) {
scale = [screen scale];
}
if (scale>0.0) {
UIGraphicsBeginImageContextWithOptions(cell.imageView.bounds.size, NO, scale);
}
else {
UIGraphicsBeginImageContext(cell.imageView.bounds.size);
}
CGRect imageRect = CGRectMake(0, 0, 84, 84 ); // cell.imageView.bounds;
FittingImageView *fittingImage = [[FittingImageView alloc] initWithFrame:imageRect];
fittingImage.thumb = YES;
fittingImage.title = offSetType.title;
[fittingImage drawRect:imageRect]; // this works but skips one imageRect
// [fittingImage setNeedsDisplay]; //this won't work.
[cell.layer renderInContext:UIGraphicsGetCurrentContext()];
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return cell;

You can never call drawRect: yourself. It is called by Cocoa, and only Cocoa can setup the context. It's unclear what you're trying to do here. I don't know what a FittingImageView is. You're creating it and then throwing it away in any case.
You then try to render the cell itself into an image, and the put that image into the cell's imageview. That doesn't make sense.
You may misunderstand how tableview cells are created, reused and drawn. You should re-read "Creating and Configuring a Table View."
The primary things to remember:
In this routine, your job is to either create a new cell, or reconfigure a reusable cell (if dequeueReusableCellWithIdentifier: returns you something).
This routine is not for drawing a cell. That will happen much later, and it is the cell's job to draw itself. You're just configuring the cell. Putting everything in it that it needs to draw later.
You typically do not want to compute expensive things in this routine. Compute those elsewhere and cache them. This routine should configure a cell, slap data into it, and return. If you try to create new images in this routine, your table view is going to stutter.
You shouldn't mess with the cell's layer in the middle of this routine. If you need something that complex, you should be creating a custom cell with its own drawRect:.

Related

Multiple NSTableCellView in one NSTableviewColumn Cocoa OSX

I am trying to create a NSTableview with different cells in one column. I am setting two NSTableCellView's in one column like so:
And setting the data code like so:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// Get a new ViewCell
// Since this is a single-column table view, this would not be necessary.
// But it's a good practice to do it in order by remember it when a table is multicolumn.
if( [tableColumn.identifier isEqualToString:#"BugColumn"] )
{
NSMutableDictionary *dic =(NSMutableDictionary*)[self.ArraRecentDataLoad objectAtIndex:row];
if([[dic valueForKey:#"type"] isEqualToString:#"image/png"] || [[dic valueForKey:#"type"] isEqualToString:#"image/jpeg"])
{
ImageCell *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
cellView = [[ImageCell alloc] initWithFrame:NSMakeRect(0, 0, 316, 313)];
// This allows the cell to be reused.
cellView.identifier =tableColumn.identifier;
NSColor *orangeColor = [NSColor colorWithCGColor:[NSColor colorWithRed:240/255.0f green:240/255.0f blue:240/255.0f alpha:1.0f].CGColor];
// Convert to CGColorRef
NSInteger numberOfComponents = [orangeColor numberOfComponents];
CGFloat components[numberOfComponents];
CGColorSpaceRef colorSpace = [[orangeColor colorSpace] CGColorSpace];
[orangeColor getComponents:(CGFloat *)&components];
CGColorRef orangeCGColor = CGColorCreate(colorSpace, components);
[cellView.bgCellview setWantsLayer:YES];
cellView.bgCellview.layer.masksToBounds= YES;
cellView.bgCellview.layer.borderWidth=2;
// Set border
cellView.bgCellview.layer.borderColor = orangeCGColor;
// Clean up
CGColorRelease(orangeCGColor);
return cellView;
}
else
{
HomeCell *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
NSColor *orangeColor = [NSColor colorWithCGColor:[NSColor colorWithRed:240/255.0f green:240/255.0f blue:240/255.0f alpha:1.0f].CGColor];
// Convert to CGColorRef
NSInteger numberOfComponents = [orangeColor numberOfComponents];
CGFloat components[numberOfComponents];
CGColorSpaceRef colorSpace = [[orangeColor colorSpace] CGColorSpace];
[orangeColor getComponents:(CGFloat *)&components];
CGColorRef orangeCGColor = CGColorCreate(colorSpace, components);
[cellView.bgview setWantsLayer:YES];
cellView.bgview.layer.masksToBounds= YES;
cellView.bgview.layer.borderWidth=2;
// Set border
cellView.bgview.layer.borderColor = orangeCGColor;
// Clean up
CGColorRelease(orangeCGColor);
return cellView;
}
}
return nil;
}
But in my xib if I drag a second cell (imageCell) then my app crashes with unrecognized select. If I do it like the above screenshot, then I am not able to load a second cell and there is white space in NSTableview.
I do lots of RND but I am not able to find a proper way to use different cells in a single column and load as per conditional data.
Note:
If I remove alloc cell in first condition:
cellView = [[ImageCell alloc] initWithFrame:NSMakeRect(0, 0, 316, 313)];
cellView.identifier =tableColumn.identifier;
Then that shows the first row's pre-define labels instead of second ImageCell predefine like iOS.
The two cell views need to have different identifiers. You will have to manually assign at least one of them. You can't let Xcode automatically assign both. (I'm not even sure if Xcode will let you configure things that way.)
Then, you need to use the proper identifier when you call [tableView makeViewWithIdentifier:... owner:self]. At most one of those calls can use the table column's identifier. The other must use the manually-assigned one.
If you put both cell views in the NIB (with separate identifiers) and fetch them using [tableView makeViewWithIdentifier:... owner:self], then you don't need to alloc/init one programmatically (and you shouldn't). If you prefer to alloc/init one programmatically, you must assign a different identifier for it. Again, you can't have two separate cell view types with the same identifier.
Update: Let me try again in simpler form. Do this:
In the NIB, select the Image Cell view. On the Identity inspector, set the identifier to "ImageCell".
In your code, for the image branch, do ImageCell *cellView = [tableView makeViewWithIdentifier:#"ImageCell" owner:self]; instead of using the table column identifier.
Remove the following two statements which allocate a new instance of the ImageCell class and sets its identifier property.
So, the code should look like this:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// Get a new ViewCell
// Since this is a single-column table view, this would not be necessary.
// But it's a good practice to do it in order by remember it when a table is multicolumn.
if( [tableColumn.identifier isEqualToString:#"BugColumn"] )
{
NSMutableDictionary *dic =(NSMutableDictionary*)[self.ArraRecentDataLoad objectAtIndex:row];
if([[dic valueForKey:#"type"] isEqualToString:#"image/png"] || [[dic valueForKey:#"type"] isEqualToString:#"image/jpeg"])
{
ImageCell *cellView = [tableView makeViewWithIdentifier:#"ImageCell" owner:self];
NSColor *orangeColor = [NSColor colorWithCGColor:[NSColor colorWithRed:240/255.0f green:240/255.0f blue:240/255.0f alpha:1.0f].CGColor];
// ...
See first of all make it view base table view from nib. Then in viewfortablecolumn put conditions like of image, then check first of view(image cell or home cell make it simple NSView) available for image type then use it or alloc using makeviewusingidentifire or what ever you use and return that view. On second condition you have do same for that.
For every time call this method it will check first which type of view (image cell or home cell in this particular) it wants then check if it was available in memory then use it if not then alloc and use it.
Let me know if any problem on this.
Sry for bad English

UITableView memory issues

I'm adding UIView in some Cells of my UITableView. I use dequeueReusableCellWithIdentifier and it seems that when I scroll up and down and up and down, the views are never removed from memory and it keeps adding new views in front of the already loaded views. It increases the memory forever and that's not the way it should be.
How to remove these UIViews's from memory when not displayed ?
else if (indexPath.row == 0 && !isPremium) {
// Google AdMob
adCell = [tableView dequeueReusableCellWithIdentifier:adCellId];
if (adCell == nil) {
adCell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:adCellId];
}
AdMobView = [[GADBannerView alloc]initWithAdSize:kGADAdSizeSmartBannerPortrait];
AdMobView.adUnitID = #"ca-app-pub-XXXXXXXXXXXXXXX";
AdMobView.rootViewController = self;
AdMobView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
// load the ads
GADRequest *request = [GADRequest request];
request.testDevices = [NSArray arrayWithObjects:GAD_SIMULATOR_ID, #"XXXXXXXXX", nil]; // COMMENT THIS LINE WHEN IN PROD
// add gps
if ([latitude length] > 2 && [longitude length] > 2) {
[request setLocationWithLatitude:[latitude floatValue] longitude:[longitude floatValue] accuracy:50]; // accuracy [m]
}
adCell.backgroundColor = [UIColor colorWithRed:247.0/255.0 green:149.0/255.0 blue: 24.0/255.0 alpha:1];
if (!AdMobView.superview) {
[AdMobView loadRequest:request];
}
AdMobView.tag = 3000;
[adCell.contentView addSubview:AdMobView];
[adCell setSelectionStyle:UITableViewCellSelectionStyleNone];
return adCell;
}
If the cells are being reused but the views are still piling up, that would be because in your cellForRowAtIndexPath: you are adding the views even to recycled cells that already have that view. Thus the views keep increasing in number, piling up on top of each other in each cell, as you scroll up and down.
Thus (now that you've added some code) you are saying:
[adCell.contentView addSubview:AdMobView];
...for every cell, unconditionally. But if a cell is reused, it already has an AdMobView! You need to check for this so you add this subview only conditionally (and so too for any other subviews you add).

Resize downloaded image from URL using SDWebImage from YouTube JSON EDITED

I'm downloading thumbnail images from youtube JSON web service using SDWebImage, in order to set them in a table view cell. The problem is that the download image is 120x90, too big for my needs , as I want to fit it in a UIImageView of 75x75. While the images are cached in the placeholder, they look correct, but once cache is finished, they look in the 120x90 ratio size. I tried setContentMode:UIViewContentModeScaleToFill, but it doesn't work, so I don't know how to solve this issue.
NSString *path= [thumbnail valueForKey:#"sqDefault"];
[cell.imageView setImageWithURL:[NSURL URLWithString:path] placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
[cell.imageView setContentMode:UIViewContentModeScaleToFill];
CALayer * l = [cell.imageView layer];
[l setMasksToBounds:YES];
[l setCornerRadius:5.0];
Many thanks
EDITED:
I have set content mode in the custom cell extended class:
newsCell.m
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.Image setContentMode:UIViewContentModeScaleToFill];
}
return self;
}
And here is the cell creation code:
multimedia.m
newsCell *cell = [self.tableView
dequeueReusableCellWithIdentifier:#"news"];
if (cell == nil) {
cell = [[newsCell alloc] initWithStyle:UITableViewCellSelectionStyleNone reuseIdentifier:#"news"];
}
The issue is that if (cell == nil) is never called because first statement is creating cell, so initWithStyle is not called and content mode is not set, but if I delete first statement, cell is not created. I don't know how to manage it.
Thanks
Try these two options and see which better fits your needs:
UIViewContentModeScaleAspectFit
UIViewContentModeAspectFill
If these don't work, I am guessing it is because the UITableViewCell is setting the contentMode on the UIImageView -- in this case you should create a subclass of UITableViewCell that contains a UIImageView with the proper contentMode set. Then you can use your custom UITableViewCell subclass in your UITableView.
Set custom size of your table image view in its custom cell class
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = CGRectMake(5,5,40,32.5);
float limgW = self.imageView.image.size.width;
if(limgW > 0) {
self.textLabel.frame = CGRectMake(55,self.textLabel.frame.origin.y,self.textLabel.frame.size.width,self.textLabel.frame.size.height);
self.detailTextLabel.frame = CGRectMake(55,self.detailTextLabel.frame.origin.y,self.detailTextLabel.frame.size.width,self.detailTextLabel.frame.size.height);
}
}
For more details please refer this link

UITableView subview appears in wrong section when scrolling?

In a UITableView I add a UIView as subview but ONLY for section 1. Section 1's content is loaded from a plist and the plist contains mutable content. If there are enough rows to allow scrolling, then the following happens: I scroll to the bottom, and back up, and the UITextField appears randomly on some of section 0's cells. I have no clue why this is happening! So what i do is this (in ´cellForRowAtIndexPath´):
if (indexPath.section == 0) {
//do stuff
}
else if (indexPath.section == 1) {
d = [UIView alloc] init];
[cell.contentView addSubview:d];
}
and this gets totally messed up when I scroll. The subviews appear in section 0 where they shoudnt, and on didSelectRowAtIdexPath I reload for section 1 and then subviews even appear twice (over each other)... Its a complete MESS! Please, Please help.......
Without seeing any code this seems to be an issue pertaining to reusable cells. What happens is that the cells that have scrolled off the screen are reused for the new content that is to be shown. So i reckon you need to make a distinction in cellForRowAtIndexPath for section 0 and 1 and basically use different set of cells for them.
EDIT: Ok ima give a shot to your problem here
UITableViewCell *cell;
if (indexPath.section == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:#"CellWithoutSubview"];
if (cell ==nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStylePlain reuseIdentifier:#"CellWithoutSubview"] autorelease];
}
//do stuff with cell like set text or whatever
}
else if (indexPath.section == 1) {
cell = [tableView dequeueReusableCellWithIdentifier:#"CellWithSubview"];
if (cell ==nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStylePlain reuseIdentifier:#"CellWithSubview"] autorelease];
d = [[UIView alloc] init];
[cell.contentView addSubview:d];
[d release];
}
}
return cell;
So now you'll have two types of cells for the tableview that'll be reused one without the subview and one with the subview.
You must be using dequeueReusableCellWithIdentifier. The purpose of dequeueReusableCellWithIdentifier is to use less memory. If the screen can fit 4 or 5 table cells, then with reuse you only need to have 4 or 5 table cells allocated in memory even if the table has 1000 entries.
So the subviews in UITableViewCell are also cached. So when the cell is reused, you need to clean out the old view & then put in the new content.
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier: #"your-id"];
if (cell)
{
//reusing old cell. reset all subviews before putting new content.
}
else
{
//Create a fresh new cell
}
You should use switch instead:
switch ( indexPath.section )
{
case 0:
{
/* do soemthing */
}
break;
case 1:
{
d = [UIView alloc] init];
[cell.contentView addSubview:d];
}
break;
}

Clear a CALayer

I use a CALayer and CATextLayers to lay out the numbers on a sudoku grid on the iPhone.
I have a tableView that lists some sudokus. When I tap one table cell it shows the sudoku in another viewController that is pushed on to the navigation controller.
In my - (void)viewWillAppear... method I call my - (void)loadSudoku method which I will show you below.
The problem is when you look at one sudoku, go back to the table view using the "back" button in the navigationBar and then tap another sudoku. Then the old sudoku is still there, and the new one is drawn on top of the old one.
I guess I need to clear the old one somehow. Any ideas?
I do have a background image set through the interface builder of the actual sudoku grid. I don't want to remove this.
The method that draws the sudoku looks like this:
- (void)loadSudoku
{
mainLayer = [[self view] layer];
[mainLayer setRasterizationScale:[[UIScreen mainScreen] scale]];
int col=0;
int row=0;
for(NSNumber *nr in [[self sudoku] sudoku])
{
if([nr intValue] != 0)
{
//Print numbers on grid
CATextLayer *messageLayer = [CATextLayer layer];
[messageLayer setForegroundColor:[[UIColor blackColor] CGColor]];
[messageLayer setContentsScale:[[UIScreen mainScreen] scale]];
[messageLayer setFrame:CGRectMake(col*36+5, row*42, 30, 30)];
[messageLayer setString:(id)[nr stringValue]];
[mainLayer addSublayer:messageLayer];
}
if(col==8)
{
col=0; row++;
}else
{
col++;
}
}
[mainLayer setShouldRasterize:YES];
}
To remove only text layers, you can do this –
NSIndexSet *indexSet = [mainLayer.sublayers indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop){
return [obj isMemberOfClass:[CATextLayer class]];
}];
NSArray *textLayers = [mainLayer.sublayers objectsAtIndexes:indexSet];
for (CATextLayer *textLayer in textLayers) {
[textLayer removeFromSuperlayer];
}
In a nutshell, the first statement gets all the indices of text layers which are a sublayer to over root layer. Then in the second statement we get all those layers in a separate array and then we remove them from their superlayer which is our root layer.
Original Answer
Try doing this,
mainLayer.sublayers = nil;