I am using some static UITableViewCell's configured in the Storyboard to display some setting information.
Some of the other cells should be disabled if one of the other settings is toggled off.
In order to put the cells into the proper state, during viewWillAppear I read the settings from NSUserDefaults and then change the cells accordingly.
- (void) viewWillAppear:(BOOL)animated
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"OtherCellEnabled"]) {
[self otherCell].alpha = 1.0;
[self otherCell].userInteractionEnabled = YES;
}
else {
NSLog(#"Changing alpha to 0.3");
[self otherCell].alpha = 0.3;
[self otherCell].userInteractionEnabled = NO;
}
The problem is that when I actually run the program, even though it says in the log that the alpha is changed, the alpha doesn't actually change. The userInteractionEnabled does seem to stick, but the alpha is left at 1.0.
It's not a problem of cell reuse, or cell's not being instantiated in time, because the other settings can be changed just fine.
Changing it from cell.alpha to cell.contentView.alpha works, but that is a different setting.
It seems like all of the settings "stick" except for the alpha setting, which somehow is getting overwritten.
I am answering my own question because I was able to solve it.
First, I tried putting the alpha change in cellForRowAtIndexPath, but that didn't work either. After a lot of tinkering, I've come to the conclusion that UITableViewCell's alpha setting is somehow special in that it keeps getting overwritten or set to 1.0.
I found two fixes:
First, instead of doing the change in cellForRowAtIndexPath, do it in the UITableViewDelegate method willDisplayCell. For whatever reason, changing the cell's alpha in this method will actually stick. Of course, if you do it this way you have to re-arrange your logic so that the changes are done on a cell-by-cell basis, i.e.:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (cell == [self otherCell]) {
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"OtherCellEnabled"]) {
cell.alpha = 1.0;
cell.userInteractionEnabled = YES;
}
else {
NSLog(#"Changing alpha to 0.3");
cell.alpha = 0.3;
cell.userInteractionEnabled = NO;
}
}
}
As I said, I'm not sure exactly why this works in willDisplayCell but not in cellForRowAtIndexPath. Others seem uncertain also:
What is -[UITableViewDelegate willDisplayCell:forRowAtIndexPath:] for?
UITableView background with alpha color causing problem with UITableViewCell
The other solution is to, instead of using the problematic alpha, use another setting which will achieve the same effect. In my case, that was the contentView.alpha and the backgroundColor. For whatever reason, these settings will stick, and you can even set them in viewWillAppear and it will work as expected:
- (void) viewWillAppear:(BOOL)animated {
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"OtherCellEnabled"]) {
[self otherCell].backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
[self otherCell].contentView.alpha = 1.0;
[self otherCell].userInteractionEnabled = YES;
}
else {
NSLog(#"Changing alpha to 0.3");
[self otherCell].backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.3];
[self otherCell].contentView.alpha = 0.3;
[self otherCell].userInteractionEnabled = NO;
}
}
The disadvantage to the second approach is that now you are overwriting the Storyboard's cell color settings, but you could work around that by asking the storyboard for the color if you care about that.
I'm not sure why cell.alpha is treated differently. Maybe something about the way static cells are implemented.
You could try to hint the cell should be redrawn after your if { .. } else { .. }, by using setNeedsDisplay:
[self otherCell setNeedsDisplay]
Based on your comment, how do you get to otherCell?
Is this a post that might help?
Related
This is my first time working with UICollectionView.
I've got everything set up and laid out as it should be (as far as I know). I've had a little bit of difficulty with the way the dequeue function works for the UICollectionView, but I think I've gotten past that. It was tricky setting up my custom cell classes when I didn't know if initWithFrame would be called or prepareForReuse.
I'm pretty sure the problem lies within the prepareForReuse function, but where is the question.
What happens is, the cells will apparently randomly draw at the top-left of the collection view and some cells will not be where they belong in the grid. (see image attached)
When bouncing, scrolling, and zooming (so as to cause reuse to occur), the problem happens. Randomly a slide will appear in the top left, and other slides will randomly disappear from the grid.
( I need more rep to post an image. Ugh. :| If you can help me, I'll email you the image. bmantzey#mac.com )
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
Slide* thisSlide = [_presentation.slidesInEffect objectAtIndex:indexPath.row];
[BuilderSlide prepareWithSlide:thisSlide];
BuilderSlide* cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"PlainSlide" forIndexPath:indexPath];
return cell;}
I'm using a static method to set the Slide object, which contains the data necessary to either prepare the asynchronous download or retrieve the image from disk cache.
It's simply:
+(void)prepareWithSlide:(Slide*)slide{
if(s_slide)
[s_slide release];
s_slide = [slide retain];}
I'm not sure if it's a big no-no to do this but in my custom Cell class, I'm calling prepareForReuse in the initWithFrame block because I need that setup code to be the same:
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if(self)
{
[self prepareForReuse];
}
return self;}
Here's the prepareForReuse function:
-(void)prepareForReuse{
CGSize size = [SpringboardLayout currentSlideSize];
[self setFrame:CGRectMake(0, 0, size.width, size.height)];
self.size = size;
// First remove any previous view, so as not to stack them.
if(_builderSlideView)
{
if(_builderSlideView.slide.slideID == s_slide.slideID)
return;
[_builderSlideView release];
}
for(UIView* aView in self.contentView.subviews)
{
if([aView isKindOfClass:[BuilderSlideView class]])
{
[aView removeFromSuperview];
break;
}
}
// Then setup the new view.
_builderSlideView = [[BuilderSlideView alloc] initWithSlide:s_slide];
self.builderCellView = _builderSlideView;
[s_slide release];
s_slide = nil;
[self.contentView addSubview:_builderSlideView];
if([SlideCache isImageCached:_builderSlideView.slide.slideID forPresentation:_builderSlideView.slide.presentationID asThumbnail:YES])
{
[_builderSlideView loadImageFromCache];
}
else
{
[_builderSlideView loadView];
}}
Finally, when the slide image has been downloaded, a Notification is posted (I plan on changing this to a delegate call). The notification simply reloads the cell that has received an update. Here's the notification code:
-(void)didLoadBuilderCellView:(NSNotification*)note{
BuilderCellView* cellView = [[note userInfo] objectForKey:#"cell"];
BuilderSlideView* slideView = (BuilderSlideView*)cellView;
NSIndexPath* indexPath = [self indexPathForSlide:slideView.slide];
if(indexPath)
[self.collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];}
Note that the slide objects exist in the model.
Any ideas as to what may be causing this problem? Thanks in advance!
The problem that was causing the cells to draw top left and/or disappear was caused by infinite recursion on a background thread. Plain and simply, I wasn't implementing the lazy loading correctly and safely. To solve this problem, I went back to the drawing board and tried again. Proper implementation of a lazy loading algorithm did the trick.
In my custom UITableViewCell I have a UIButton. For a particular cell (row) object I want to change the width of the button. I tried many ways, but can't change the size of button in any ways. I am not using AutoLayout in this cell, as this button contains text and image and using auto layout am not able to set the spacing between text ands image properly.
Code of cellForRowAtIndexPath that initiates cell and calls resize function :
} else if ([text isEqualToString:#"Now"] ) { //(indexPath.row == 2) {
vcell.listDataSource = inList;
count = inList.count;
vcell.listsTableView.tag = 2;
[vcell reSizeButton];
Method in custom UITableViewCell :
-(void) reSizeButton {
CGRect btnFrame = self.button.frame;
btnFrame.size = CGSizeMake(157.0, btnFrame.size.height);
[self.button setFrame:btnFrame];
//[self.button sizeToFit];
//[self.button setNeedsDisplay];
[self.button setNeedsLayout];
return;
}
Method is being called properly. As it is being called during initing cell properties, so I don't think I need to call to reload the cell.
UPDATE :
After calling the reSizeButton method, I am calling the setText method :-
-(void) setButtonText :(NSString *) text withCount:(int)count isExpanded:(BOOL)expanded {
titleText = text;
countNums = count;
cellExpanded = expanded;
[self updateButtonText];
return;
}
-(void) updateButtonText {
countNums = (int)[self.listDataSource count];
NSString *str = [NSString stringWithFormat:#"%# (%d) ", titleText, countNums ];
[self.button.titleLabel setFont:[UIFont fontWithName:#"OpenSans" size:12.0] ];
[self.button setTitle:str forState:UIControlStateNormal];
//[self.button sizeToFit];
[self.button setNeedsLayout];
return;
}
Maybe his be causing the problem....
Can you point where am I going wrong ? Since long am trying with this, but couldn't solve it. Thanks.
Thanks #Greg and #Grzegorz Krukowski . Thanks for your time and effort to help me.
Don't know what was the problem, but I again un-checked (which was already unchecked) AutoLayout checkbox from xib for the cell. Tried again. My Code remains same as shown in question. And it started to work as expected.
Godness, can't recognize the problem and solved the issue.
I've tried just about everything I can think of, googled lots, searched SO, and still can't find the solution. Here's the issue:
I have a custom UITableViewCell with several labels in it. The text varies dynamically, and the background color (of the cell, not the labels) should vary as well. The text changes in the labels work fine. However, the BG color won't change no matter what I do. The only upside is that I don't feel that lonely. This is apparently a mystery even to a few high-rep people. I'm hoping someone here has found a solution (or can point out my mistake).
Here's what I have done:
Put the logic inside cellForRowAtIndexPath. Disappointment.
Googled and SO'd some more, then:
Put the logic inside a willDisplayCell call. Nothing.
Googled more, then:
Put the logic back into cellForRowAtIndexPath. Nada.
Put the logic inside the willDisplayCell call again. No go.
Found a post on SO that suggested putting another view in the custom cell to cover the original background and set it to change with logic. It didn't.
Tried putting the logic back into cellForRowAtIndexPath.
Tried using a switch statement in the logic.
Tried using if, else if, else in the logic.
Several other things I can't remember. Still doesn't work.
Yes, the UItableViewdelegate is set.
Here's the current code, which also doesn't work:
EDIT: Made a slight change in the top if statement to reflect the suggestion by #Adrian below. Unfortunately, it didn't cure the problem.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id<NSFetchedResultsSectionInfo> sectionInfo = [[detailFRC sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
NSLog(#"There are %lu objects in the frc",(unsigned long)[sectionInfo numberOfObjects]);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell * cell = [tableView dequeueReusableCellWithIdentifier:#"myCustomCell"];
if (!cell)
{
[tableView registerNib:[UINib nibWithNibName:#"CustomCell" bundle:nil] forCellReuseIdentifier:#"myCustomCell"];
cell = [tableView dequeueReusableCellWithIdentifier:#"myCustomCell"];
}
return cell;
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(CustomCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
thisActivity = [detailFRC objectAtIndexPath:indexPath];
if (self.activityOrCategory == 0)
{
if (thisActivity.name == self.detailFocusItem)
{
[cell.myBGView setBackgroundColor:Rgb2UIColor(255, 215, 215)]; // Light red
cell.backgroundView = cell.myBGView;
}
else if (thisActivity.name == self.detailBenchmarkItem)
{
[cell.myBGView setBackgroundColor:Rgb2UIColor(215, 220, 255)]; // Light blue
}
else
{
cell.myBGView.backgroundColor = [UIColor whiteColor];
}
}
else if (self.activityOrCategory == 1)
{
if (thisActivity.category == self.detailFocusItem)
{
cell.myBGView.backgroundColor = Rgb2UIColor(255, 235, 200);
}
else if (thisActivity.category == self.detailBenchmarkItem)
{
cell.myBGView.backgroundColor = Rgb2UIColor(200, 255, 200);
}
else
{
cell.myBGView.backgroundColor = [UIColor whiteColor];
}
}
NSLog(#"cell.backgroundColor is %#",cell.backgroundColor);
NSLog(#"This row says %#",thisActivity.name);
cell.activityLabel.text = thisActivity.name;
cell.categoryLabel.text = thisActivity.category;
NSDateFormatter *dateFormat = [[NSDateFormatter alloc]init];
[dateFormat setDateFormat: #"MM/dd/yyyy hh:mm:ss"];
cell.fromDateLabel.text = [dateFormat stringFromDate:thisActivity.startTime];
cell.toDateLabel.text = [dateFormat stringFromDate:thisActivity.stopTime];
}
Many thanks for taking the time to look! All help appreciated, even if it's my stupid mistake.
I was under the impression that a UITableViewCell has an actual property of backgroundView, I can see you have something called BGView, but I can't see anywhere from your example where the cell property gets set.
So all I can say is that for my examples where I have done this, I always create a UIView and set it's colour, then assign it to the backgroundView property.
I hope this might be of some help and that I didn't miss you doing that in your example earlier!
// Effectively draws the cell with a red background
UIView *backView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 44)];
backView.backgroundColor = [UIColor redColor];
cell.backgroundView = backView;
Thanks
Adrian_H
I never use Rgb2UIColor before, I don't know how it works.
Try the below code, it works on my app:-
inside willDisplayCell
CGFloat nRed=255/255.f;
CGFloat nGreen= 215/255.f;
CGFloat nBlue = 215/255.f;
UIColor * myColor = [UIColor colorWithRed:nRed green:nGreen blue:nBlue alpha:1]; //Light Red
cell.textLabel.backgroundColor = myColor;
cell.detailTextLabel.backgroundColor = myColor;
cell.backgroundColor =myColor;
[cell.backgroundView setBackgroundColor:myColor];
I hope those who looked at and especially those who took the time to answer this question can have a sense of humor about this:
It turns out that both of the answers graciously contributed above will work (with minor mods). I feel like a bit of a dummy, but the problem I was encountering wasn't with the setting of the background color, it was with the logic itself. Namely, I was using this code in my if statement:
if (thisActivity.name == self.detailFocusItem)
when, since both the objects in question are NSStrings, it should have been:
if ([thisActivity.name isEqualToString:self.detailFocusItem])
Thus, control was simply skipping the color-setting code. :/
I really do apologize for taking your time.
OTOH, had it not been for having been forced to take a new look at it as I implemented your suggestions, I don't know how long it might have taken me to see the forest instead of the trees.
You both get upvotes, and I hope you'll be gentle and chalk it up to the inexperience of a nooby enthusiast.
Many thanks for helping!
First off I want to say I saw a couple of posts on this site about how to do this, although none seemed to work for me so please don't close this down until I get it working.
What I want to do is make the background of the view change depending on the value of the sliders are, so that the user can choose the background colour they want.
self->colorView.backgroundColor = [UIColor myColor];
myColor =
I figure I'll need a bit of code like that, although I don't know how to define what my colour will be something; like "red: redSlider / 255" and so on for the other colours? I also don't know where to implement the code above as I need it to continuously update when the use changes the values of the sliders.
I am quite basic at programming as you may have picked up because I'm only a teenager doing it as a hobby and I'd appreciate simple instructions telling me clearly where I need to put code etc.
p.s. It won't let me post an image of the view, sorry :(
In your ViewController.h file define
#property (nonatomic, strong) IBOutlet UISlider *mySlider;
In ViewController.m file, add this:
- (void) sliderValueChanged:(UISlider *)slider
{
// Handle your color changing logic here
myView.backgroundColor = [UIColor colorWithRed:0.4f green:0.5f blue:1.0f alpha:1.0f];
}
In Interface Builder,
Drag UISlider to view and set its "Value Changed" event outlet to sliderValueChanged method.
Now as you change the slider on screen, the color should changed based on your logic in the method sliderValueChanged
Below is the logic as per your requirement:
- (void) sliderValueChanged:(UISlider *)slider
{
// Assuming slider minimum is 0 and maximum is 1
CGFloat redVal = 0.0f;
CGFloat yellowVal = 0.0f;
CGFloat blueVal = 0.0f;
if (slider == redSlider)
{
redVal = slider.value;
}
else if (slider == yellowSlider)
{
yellowVal = slider.value;
}
else if (slider == blueSlider)
{
blueVal = slider.value;
}
myView.backgroundColor = [UIColor colorWithRed:redVal green:greenVal blue:blueVal alpha:1.0f];
}
As UISlider implements the UIAppearence protocol you can set its background color like:
mySlider.backgroundColor = [UIColor lightGrayColor]; // Or any other color
or:
[[mySlider appearance] setBackgroundColor:[UIColor lightGrayColor]];
Disclaimer: I've been working too late. But, I'm determined to get through this one tonight.
I have an app where I support different color themes. The dark cell backgrounds have been problematic.
I've been poking around trying to find a formidable way to draw the accessory disclosure icon in uitableviewcells with black backgrounds.
I decided to try overriding setAccessoryType to inherit the functionality for my 50+ views:
-(void) addWhiteDisclosureImage {
UIImageView *disclosureView = (UIImageView*) [self.contentView viewWithTag:kDisclosureReplacementImageTag];
if(!disclosureView) {
[super setAccessoryType:UITableViewCellAccessoryNone];
disclosureView = [[UIImageView alloc] initWithImage:self.whiteDisclosureImage];
disclosureView.tag = kDisclosureReplacementImageTag;
disclosureView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
DebugLog(#"%f, %f", self.frame.size.width, self.frame.size.height);
[self.contentView addSubview:disclosureView];
[self.contentView bringSubviewToFront:disclosureView];
[disclosureView release];
}
}
- (void)setAccessoryType:(UITableViewCellAccessoryType)accessoryType {
if(accessoryType == UITableViewCellAccessoryDisclosureIndicator) {
if ([self.viewController isKindOfClass:[ViewControllerBase class]]) {
ViewControllerBase *view = (ViewControllerBase*) self.viewController;
if(view.colorTheme && view.colorTheme.controlBackgroundColor) {
if([ViewColors colorAverage:view.colorTheme.controlBackgroundColor] < 0.2) { //substitute white disclosure indicator
[self addWhiteDisclosureImage];
return;
} else { //not dark enough
[self removeWhiteDisclosureImage];
[super setAccessoryType:accessoryType];
return;
}
} else { //no colorTheme.backgroundColor
[self removeWhiteDisclosureImage];
[super setAccessoryType:accessoryType];
return;
}
} else { //viewController is not type ViewControllerBase
[self removeWhiteDisclosureImage];
[super setAccessoryType:accessoryType];
return;
}
}
UIView *disclosureView = [self.contentView viewWithTag:kDisclosureReplacementImageTag];
if(disclosureView)
[disclosureView removeFromSuperview];
[super setAccessoryType:accessoryType];
}
This override is typically called in cellForRowAtIndexPath.
It seemed like a good option until I drill down and come back. For some cells, the cell frame will be a great deal larger than the first time through. This consistently happens to the same cell in a list of 6 that I've been testing against. There's clearly something unique about this cell: it's frame.size.
Here is the size of the cell that I log for the first tableview load (in some cases every load/reload):
320.000000, 44.000000
This is the difference in what I get for some (not all) of the cells after call to reloadData:
759.000000, 44.000000
Does anyone know why this might happen?
Update: the suspect cell's custom accessory disclosure view almost acts like it's autoresizing flag is set to none. I confirmed this by setting all to none. I say almost because I see it line up where it should be after reloadData. A split second later it moves clear over to the left (where they all end up when I opt for no autoresizing).
Don't mess around with subviews and calculating frames.
Just replace the accessoryView with the new imageView. Let iOS do the work.