I am on iOS7 and have a UITableViewCell subclass for my UITableView with static cells. I am overriding the setSelected method in the implementation.
For some reason, the method only gets called when the table loads but doesn't get called when the cell is actually tapped and selected.
What am I doing wrong here? How do I get it to work?
#implementation StudentMenuMultipleOptionsTableViewCell
- (void)setSelected:(BOOL)selected {
[super setSelected:selected];
if (selected) {
UIView *view = [UIView new];
view.backgroundColor = [UIColor colorWithRed:0.542 green:0.788 blue:0.060 alpha:1.000];
self.selectedBackgroundView = view;
}
else {
for (UIView *view in self.subviews) {
if ([view isKindOfClass:[BlackBackgroundSelectedButton class]]) {
BlackBackgroundSelectedButton *button = (BlackBackgroundSelectedButton *)view;
button.selected = NO;
[button setWhite];
}
}
}
}
#end
The problem was that I was using the setSelected method. The method that needs to be used for the newer iOS versions is:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
Related
I have a simple UITableView. Everything works fine on iOS 7 but on iOS 8 not. Basically I have a custom UITableViewCell which contains text labels, buttons, images. When I press the button inside the tableview cell I got error. Here is the code:
-(IBAction)addToFavorites:(id)sender {
UIButton *editButton = (UIButton*)sender;
GetMoreTableViewCell *editCell = (GetMoreTableViewCell*)[[[editButton superview] superview] superview];
int editIndex = (int)[[_chapters indexPathForCell:editCell] row];
[self readTopScorePlist];
if([[[[_data valueForKey:key] valueForKey:[NSString stringWithFormat:#"%i",editIndex]] valueForKey:#"isDefault"] boolValue] == YES) {
[editCell.addButton setImage:[UIImage imageNamed:#"fav_0.png"] forState:UIControlStateNormal];
[self saveToPlist:NO sender:sender];
NSLog(#"NEW %#",[[[_data valueForKey:key] valueForKey:[NSString stringWithFormat:#"%i",editIndex]] valueForKey:#"isDefault"]);
}
else {
[editCell.addButton setImage:[UIImage imageNamed:#"fav_1.png"] forState:UIControlStateNormal];
[self saveToPlist:YES sender:sender];
NSLog(#"NEW %#",[[[_data valueForKey:key] valueForKey:[NSString stringWithFormat:#"%i",editIndex]] valueForKey:#"isDefault"]);
}
}
The method is called however I am getting exception here:[editCell.addButton setImage:[UIImage imageNamed:#"fav_0.png"] forState:UIControlStateNormal];
And the expection that I am getting is:
2014-09-22 10:08:28.223 HandyHoch[657:252982] -[UITableViewWrapperView addButton]: unrecognized selector sent to instance 0x17e9fd80
I am guessing that addButton cannot be finded, but who should I do that in iOS 8? Thanks in advance.
The UITableViewCell custom class:
#implementation GetMoreTableViewCell
#synthesize ttitle, cellImage, cellBackground, changeButton, deleteButton,addButton;
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if(self) {
ttitle.textColor = [UIColor whiteColor];
ttitle.numberOfLines = 30;
ttitle.lineBreakMode = NSLineBreakByCharWrapping;
ttitle.textAlignment = NSTextAlignmentCenter;
[self.contentView addSubview:ttitle];
[self.contentView addSubview:addButton];
[self setAutoresizesSubviews:YES];
}
return self;
}
#end
It seems that the view hierarchy changed in iOS 8, so (GetMoreTableViewCell*)[[[editButton superview] superview] superview]; returns different view. Easiest solution would be change the number of superview calls for iOS 8. The more reliable solution would be connect target to GetMoreTableViewCell.
I am trying to get a custom image working with the accessoryButtonTappedForRowWithIndexPath function. From what I understand in order to do this, you have to use a UIButton, simply adding a UIImageView on the UITableViewCellAccessoryDetailDisclosureButton will not work.
My problem is how to get the touch gesture from my UITableViewCell subclass into the parent UITableView function.
Here is the code in my UITableViewCell subclass:
upVote = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *upVoteImg = [UIImage imageNamed:#"vote.png"];
upVote.frame = CGRectMake(self.frame.size.width-upVoteImg.size.width, 0, upVoteImg.size.width , upVoteImg.size.height);
[upVote setBackgroundImage:upVoteImg forState:UIControlStateNormal];
[self.contentView addSubview:upVote];
[upVote addTarget:self action:#selector(checkButtonTapped:event:) forControlEvents:UIControlEventTouchUpInside];
The calling function (also inside the subclass of the UITableViewCell)
- (void)checkButtonTapped:(id)sender event:(id)event
{
UITableView *tableView = (UITableView *)self.superview;
[tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:[tableView indexPathForCell:self]];
}
It crashes at the final line of the above function with this:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewWrapperView
delegate]: unrecognized selector sent to instance 0x16f48160'
Yes it should crash, because delegate of tableview should not be called from custom cell class, better you can use custom delegate. I am posting a sample code, similar to your case, when button tapped it call's the custom delegate method from that u can do whatever you want
//in custom calss
#import <UIKit/UIKit.h>
//create a delegate
#protocol CustomDelegate<NSObject>
-(void)whenButtonTapped:(id)sender; //your delegate method
#end
#interface CustomCell : UITableViewCell<CustomDelegate>
#property(nonatomic, assign)id<CustomDelegate> delegate; //create delegate
#end
//.m file of custom cell
#import "CustomCell.h"
#implementation CustomCell
#synthesize delegate; //sysnthesize delegate
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; //its your button
UILabel *aLabel = [[UILabel alloc]initWithFrame:CGRectZero]; //for example i took label
[aButton addTarget:self action:#selector(whenButtonTapped:) forControlEvents:UIControlEventTouchUpInside];//adding target for button action
[aButton setTag:100];
[aLabel setTag:200];
[self addSubview:aButton];
[self addSubview:aLabel];
//since i am using without ARC
[aLabel release];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(void)dealloc
{
delegate = nil;
[super dealloc];
}
//i am setting the frames hear
-(void)layoutSubviews
{
[super layoutSubviews];
UIButton *aButton = (UIButton *)[self viewWithTag:100]; //get button
aButton.frame = CGRectMake(200, 5, 55, 40);
[aButton setTitle:#"tapMe" forState:UIControlStateNormal];
UILabel *label = (UILabel *) [self viewWithTag:200]; //get label
label.frame = CGRectMake(40, 5, 50, 35);
label.text = #"hello";
}
//hear is ur button's target
- (void)whenButtonTapped:(id)sender
{
//dont call UITableView delegate method in custom cell it is wrong, ur app get crashed
//insted create custom delegate method to your controller
[self.delegate whenButtonTapped:sender];//call the delegate method when button tapped
}
#end
//.h file where u are using custom cell
#import <UIKit/UIKit.h>
#import "CustomCell.h"
#interface ViewController : UIViewController<UITableViewDataSource , UITableViewDelegate ,CustomDelegate>//set confirms to delegate
#property (retain, nonatomic) IBOutlet UITableView *aTableView;
#end
//.m file
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *aCell = [self.aTableView dequeueReusableCellWithIdentifier:#"cell"];
if(aCell == nil)
{
aCell = [[CustomCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
aCell.delegate = self;//set delegate to this class
return aCell;
}
//hear is your delegate method
//define your delegate method hear
-(void)whenButtonTapped:(UIButton *)sender
{
//in this method u can do what ever the activity when button tapped
//sender which gives u entire cell information
UITableViewCell *cell = sender.superview;//you can get cell also :)
NSLog(#"button tapped");
}
Hopes this helps
How about a better one?
UITableView *tableview;
if ([self.superview respondsToSelector:#selector(indexPathForCell:)] ) {
tableview = (UITableView *)self.superview;
} else if ([self.superview.superview respondsToSelector:#selector(indexPathForCell:)]) {
tableview = (UITableView *)self.superview.superview;
}
if (tableview) {
// win
}
I have a category on UITableViewCell that looks like this.
- (UITableView *)tableView
{
UIView *view = self.superview;
while (view)
{
if ([view isKindOfClass:[UITableView class]])
{
return (UITableView *)view;
}
else
{
view = view.superview;
}
}
return nil;
}
- (NSIndexPath *)indexPath
{
return [[self tableView]indexPathForCell:self];
}
Then I just hook my custom button up to a method in my custom tableview cell (which imports my category) to a method such as this.
- (IBAction)customButtonPressed:(id)sender
{
[self.tableView.delegate tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:self.indexPath];
}
I am trying to customize the UITableViewCell below for an iPhone app in a grouped table view. What I would like to do is have the image width take up the whole cell minus padding (280) and the height variable based on the image size.
Currently I am using SDWebImage to asynchronously download remote images. This may not be the correct thing to do in this case. I am also having trouble figuring out how to give the custom cell the image on initialization. The image URL is stored in self.beerPhoto in the DetailViewController.
I have searched for this a number of ways and have not found exactly what I am looking for. The closest was this: How to scale a UIImageView proportionally, but this method seems to require the cell to have the image at initialization, as I tried to make this code work but setting the image after initialization left a blank cell.
The current code includes constants I set to approximate an image in portrait orientation. In reality some of the images are portrait and some are landscape orientation.
Please let me know if there's anything additional you need to know.
Header for custom UITableViewCell:
#import <UIKit/UIKit.h>
#interface BeerDetailHead : UITableViewCell {
UILabel *beerName;
UIImageView *beerImage;
}
#property(nonatomic, retain)UILabel *beerName;
#property(nonatomic, retain)UIImageView *beerImage;
#end
Relevant portion of implementation for custom UITableViewCell
#import "BeerDetailHead.h"
#implementation BeerDetailHead
#synthesize beerName, beerImage;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
//beerName = [[UILabel alloc]init];
//beerName.textAlignment = UITextAlignmentLeft;
//beerName.font = [UIFont systemFontOfSize:14];
beerImage = [[UIImageView alloc]init];
//[self.contentView addSubview:beerName];
[self.contentView addSubview:beerImage];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGRect contentRect = self.contentView.bounds;
CGFloat boundsX = contentRect.origin.x;
CGRect frame;
frame= CGRectMake(boundsX+10 ,10, 280, 375);
beerImage.frame = frame;
}
DetailViewController's cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
NSArray *listData =[self.tableContents objectForKey:
[self.sortedKeys objectAtIndex:[indexPath section]]];
NSLog(#"listData = %#", listData);
NSUInteger row = [indexPath row];
if ([self.sortedKeys objectAtIndex:[indexPath section]] == #"header"){
static NSString *headerTableIdentifier = #"HeaderTableIdentifier";
BeerDetailHead * headerCell = (BeerDetailHead*)[tableView
dequeueReusableCellWithIdentifier: headerTableIdentifier];
if(headerCell == nil) {
headerCell = [[[BeerDetailHead alloc] initWithFrame:CGRectZero reuseIdentifier:headerTableIdentifier] autorelease];
}
headerCell.beerName.text = [listData objectAtIndex:row];
[headerCell.beerImage setImageWithURL:[NSURL URLWithString:self.beerPhoto]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
//NSLog(#"frame = %#", headerCell.beerImage.frame);
return headerCell;
}
else{
//use standard UITableViewCell
}
}
Implement tableView:heightForRowAtIndexPath: delegate method and return calculated height for each row from this method.
After loading each image call reloadData on your tableView OR if you want to animate changes call:
[self.tableView beginUpdates];
[self.tableView endUpdates];
Also, you might want to combine several sequence height updates into one. I would use I little delay to perform this:
// triggerUpdates calls the above code
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(triggerUpdates) object:nil];
[self performSelector:#selector(triggerUpdates) withObject:nil afterDelay:0.1];
I subclassed UITableViewCell to set a cell background color to a color I need:
.h
#interface DataViewCustomCell : UITableViewCell {
UIColor* cellColor;
UIColor* standardColor;
}
- (void) setCellColor: (UIColor*)color;
#end
.m
#implementation DataViewCustomCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void) setCellColor: (UIColor*)color
{
cellColor = color;
}
- (void) spreadBackgroundColor: (UIView*)that withColor: (UIColor*)bkColor
{
NSEnumerator *enumerator = [that.subviews objectEnumerator];
id anObject;
while (anObject = [enumerator nextObject]) {
if([anObject isKindOfClass: [UIView class]])
{
((UIView*)anObject).backgroundColor = bkColor;
[self spreadBackgroundColor:anObject withColor:bkColor];
}
}
}
- (void) layoutSubviews {
[super layoutSubviews]; // layouts the cell as UITableViewCellStyleValue2 would normally look like
if(!self.selected && NULL != cellColor)
{
[self spreadBackgroundColor:self withColor:cellColor];
}
}
- (void)dealloc
{
[super dealloc];
}
#end
When I call setCellColor with the color I want, all goes well, but when I haven't found a way to set the original color back: when I set [UIColor clearColor] with UITableViewStylePlain style the results is not good-looking.
How can I achieve a good result, without a missing cell separator line?
I was having a similar problem and found edo42's answer. However I was then having an issue with the background behind the text in the cell not displaying the background color I set. I believe this is due to the style: UITableViewCellStyleSubtitle.
In case others stumble upon this question, I believe the better solution for this is found in this question:
BackgroundColor of UITableViewCellStyleSubtitle labels?
BackgroundColor of UITableViewCellStyleSubtitle labels?
Answer reprinted here:
To change the background color of the table view cell, you'll need to set it in tableView:willDisplayCell:forRowAtIndexPath: rather than tableView:cellForRowAtIndexPath: otherwise it won't have any effect, for example:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.backgroundColor = [UIColor whiteColor];
}
Finally, I solved it by myself. I used the following code in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath instead of subclassing:
UIView *bg = [[UIView alloc] initWithFrame:cell.frame];
bg.backgroundColor = [UIColor greenColor]; //The color you want
cell.backgroundView = bg;
cell.textLabel.backgroundColor = bg.backgroundColor;
[bg release];
why don't before you set the new cell color, capture the old cell color in a UIColor variable.
try
UIColor *originalColor = anObject.backgroundColor;
then later when you want to change back to that just change the cell color to originalColor.
I find it best to adjust the color from within the custom cell's .m file, rather than the tableViewController. Seems more intuitive, and it's slightly easier. Just adjust the color as necessary in the methods:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
and
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
Note: make sure you call the super of these methods too at the end of the call, e.g. if you want to make your cell green on touch:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.backgroundColor = [UIColor greenColor];
[super touchesBegan:touches withEvent:event];
}
Also, in my experience, it's not worth adjusting the color in touchesEnded, because your cell often won't adjust in-time for quick selections by the user.
you should always provide changes for cell in willDisplayCell:forRowAtIndex instead of cellForRowAtIndex method , as per Apple Documentation
This Works !!
Change the color in the following delegate method:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (...){
cell.backgroundColor = [UIColor blueColor];
} else {
cell.backgroundColor = [UIColor whiteColor];
}
}
I have a view with several embedded UITextFields, this UIView is subordinated to a UIScrollView in IB. Each text field is supposed to invoke a method called updateText defined in the viewcontroller implementation file when the user is done editing the field. For some reason, the method updateText never gets invoked. Anyone have any ideas how to go about fixing this? The method fired off just fine when the UIScrollView was not present in the project but the keyboard would cover the text fields during input, which was annoying. Now my textfields move up above the keyboard when it appears, but won't fire off the method when done editing.
Here is my implementation file:
#import "MileMarkerViewController.h"
#implementation MileMarkerViewController
#synthesize scrollView,milemarkerLogDate,milemarkerDesc,milemarkerOdobeg,milemarkerOdoend,milemarkerBusiness,milemarkerPersonal,milemarker;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Initialization code
}
return self;
}
- (BOOL) textFieldShouldReturn: (UITextField*) theTextField {
return [theTextField resignFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(keyboardWasShown:)
name: UIKeyboardDidShowNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(keyboardWasHidden:)
name: UIKeyboardDidHideNotification
object: nil];
keyboardShown = NO; // 1
[scrollView setContentSize: CGSizeMake( 320, 480)]; // 2
}
- (void)keyboardWasShown:(NSNotification*)aNotification {
if (keyboardShown) return;
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Resize the scroll view (which is the root view of the window)
CGRect viewFrame = [scrollView frame];
viewFrame.size.height -= keyboardSize.height;
scrollView.frame = viewFrame;
// Scroll the active text field into view.
CGRect textFieldRect = [activeField frame];
[scrollView scrollRectToVisible:textFieldRect animated:YES];
keyboardShown = YES;
}
- (void)keyboardWasHidden:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Reset the height of the scroll view to its original value
CGRect viewFrame = [scrollView frame];
viewFrame.size.height += keyboardSize.height;
[scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
scrollView.frame = viewFrame;
keyboardShown = NO;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
activeField = nil;
}
- (IBAction)updateText:(id) sender {
NSLog(#"You just entered: %#",self.milemarkerLogDate.text);
self.milemarker.logdate = self.milemarkerLogDate.text;
self.milemarker.desc = self.milemarkerDesc.text;
self.milemarker.odobeg = self.milemarkerOdobeg.text;
self.milemarker.odoend = self.milemarkerOdoend.text;
self.milemarker.business = self.milemarkerBusiness.text;
self.milemarker.personal = self.milemarkerPersonal.text;
NSLog(#"Original textfield is set to: %#",self.milemarker.logdate);
[self.milemarker updateText];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)dealloc {
[super dealloc];
}
#end
I think you have already known a solution because of post date.
You can implement textFieldShouldReturn method and resign first responder from textField in the method.
textFieldDidEndEditing is called after textField resign first responder.