I'm struggling with a problem I encountered while trying to create a custom UITableViewCell.
I subclassed UITableViewCell in SGTableViewCell and added it in the prototype cell in the storyboard.
As you can see the label is connected
and the cell identifier is set correctly
Then I linked the label to the SGTableViewCell.h like this
#property (strong, nonatomic) IBOutlet UILabel *nameLabel;
and in the .m file I have this code
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[self addGestureRecognizer:recognizer];
self.nameLabel = [[UILabel alloc] init];
_checkView = [[UIView alloc] initWithFrame:CGRectNull];
_checkView.backgroundColor = kGreen;
_checkView.alpha = 0.0;
[self addSubview:_checkView];
self.nameLabel.text = #"Hello";
}
return self;
}
But when I use this cell in my tableview using this code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Episode *episode = [self.selectedSeason.episodeList objectAtIndex:indexPath.row];
SGTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Episode"];
UIView *selectionColor = [[UIView alloc] init];
selectionColor.backgroundColor = kSelectionGrey;
cell.selectedBackgroundView = selectionColor;
cell.backgroundColor = kBackgroundGrey;
cell.nameLabel.text = episode.name;
NSLog(#"%#", cell.nameLabel.text);
cell.nameLabel.textColor = [UIColor redColor];
return cell;
}
I get no text at all.
I tried logging the text from each label in each cell and it gives me the right text.
I tried setting programmatically a different disclosure indicator for the custom cell and it did change so everything is allocated and working but label is not displaying.
I honestly have no idea of what's the problem. Did I miss something?
Thank you
PARTIALLY SOLVED:
OK i tried doing the same thing on an empty project and everything worked flawlessly so I checked again my project and found this line
[self.tableView registerClass:[SGTableViewCell class] forCellReuseIdentifier:#"Episode"];
Seeing it was not necessary for the empty project i commented this line and everything started working.
The only problem i have now is that if i don't use this line i can't use the custom cell as was intended. In fact my custom cell is swipable using a pan gesture recognizer but without registering my custom class to the tableview seems like the swipe doesn't work.
Sorry for the trouble, seems like i messed up again :/
You shouldn't alloc init a label that you created in the storyboard, it is already allocated automatically. When you do self.nameLabel = [[UILabel alloc] init];, you reset the self.nameLabel property to point to a new empty memory location and not to the label created in the storyboard, hence you can change its text property and see the result in NSLog but not in the storyboard because it doesn't refer to that label in the storyboard.
Try removing all initialisation from the initWithStyle method (to make sure nothing is covering it such as that subview you create), and everything related to the label in the cellForRowAtIndexPath method (same reason), and try a simple assignment like self.nameLabel.text = #"Test text" in the cellForRowAtIndexPath method, it should work. Then add all your other initialisation.
And yeah, don't forget to input your cell reuse identifier "Episode" in the storyboard.
Make sure you:
Have linked the delegate and the datasource to the view the tablview is housed in.
Have that view implement UITableViewController and UITableViewDelegate (I'm pretty sure it is both of those).
Implement the necessary methods, which you seem to have done. You need the row size, section size, and the add cell methods
After updating the array linked to your tableview, call [tableView reloadData]
Have a look at this link:Tutorial to create a simple tableview app
Related
I spent some time looking around other similar posts but was unable to find my exact problem. I'm new to iOS and objective c so it's probably very simple.
I'm subclassing UICollectionViewCell:
#interface FooCollectionViewCell : UICollectionViewCell
#property (weak, nonatomic) IBOutlet UILabel *cellLabel;
#end
And in my custom subclass of UIViewController, i'm trying to set a value for cellLabel:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
FooCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
UILabel *label = [[UILabel alloc] init];
label.text = #"whatever";
cell.cellLabel = label;
return cell;
}
My app crashes when run:
[UICollectionViewCell setCellLabel:]: unrecognized selector sent to instance 0x7fef224144f0
If I'm understanding that error, it looks it's complaining that i'm trying to set the cellLabel property on an instance of UICollectionViewCell the superclass instead of an instance of FooCollectionViewCell the subclass.
However, when I add a breakpoint at that line and look at my variables, I see:
cell FooCollectionViewCell * 0x7fcb5340dcd0 0x00007fcb5340dcd0
It also seems obvious from my code that the object has to be of type FooCollectionViewCell because that's the type cell is instantiated as.
My process for setting up my storyboard was:
Drag a CollectionViewController to my storyboard
Set its Custom Class to FooCollectionViewController. This seems to work because my code is called.
Click the prototype cell automatically created within the automatically created Collection View, set its Custom Class to FooCollectionViewCell. Set the Identifier to "FooCell", and update the reuseIdentifier variable in my ViewController to also be "FooCell".
Is there something I'm missing?
you say that you follow tutorial. So have you done these steps?
With the basic collection view classes implemented and associated with the storyboard objects, it is now time to design the cell. This is, quite simply, a matter of dragging and dropping items from the object library onto the prototype cell in the storyboard view. Additionally the size of the cell may be modified by selecting it and using the resulting resize handles. The exact design of the cell is entirely dependent on what is to be displayed. For the purposes of this example, however, each cell is simply going to display an image.
Because you are creating a new label in the code, when it should be already available in prototype cell. You only have to assign cell.cellLabel.text = #"bla bla";
UILabel *label = [[UILabel alloc] init];
label.text = #"whatever";
cell.cellLabel = label;
I figured out the problem. When I created my view controller subclass the file automatically had
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
in my viewDidLoad method. So I think that was overriding what I was doing in my storyboard. FML but I learned something. Thanks for the help!
The fix was to remove this line since I'm configuring that in my storyboard.
I'm trying to clean up my code and use MVC principles by pushing as much view related stuff as i can into the storyboard as well as custom UIView classes (ie as opposed to doing view related stuff on the UIViewController itself)
So I have a custom UITableViewCell (called CustomCell) that has several properties, one of them is my own label. Since I'm loading the cell from the storyboard, I initialize it with initWithCoder rather than initWithStyle:reuseIdentifier:, this is what I have in CustomCell.m which is a subclass of UITableViewCell (for some reason i couldn't figure out how to set a custom font using storyboard.. but that's beside the point of this question):
// CustomCell.m - subclass of UITableViewCell
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
NSLog(#"customizing cell font having text %#", self.label.text);
UIFont *customFont = [UIFont fontWithName:#"Montserrat" size:16];
self.label.textColor = [UIColor redColor];
[self.label setFont:customFont];
}
return self;
}
This simply doesn't work.. the log statement outputs null for the text simply b/c the text hasn't been loaded yet. self.label is also null (I don't know why I thought it should have been inflated from the nib by now) but even if I initialize it here.. it still won't work.
So my work around was to simply put this cell customization part here in the TableViewController:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
UIFont *customFont = [UIFont fontWithName:#"Montserrat" size:16];
[((CustomCell *)cell).label setFont:customFont];
}
and it worked just fine.. I'm unhappy with this method and I would like to know how to make it work from within CustomCell.m
update: to make things even more interesting.. if i put customization code for UITableViewCell properties inside initWithCoder, they work! consider this example:
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
UIView *bgColorView = [[UIView alloc] init];
[bgColorView setBackgroundColor:[UIColor blueColor]];
[self setSelectedBackgroundView:bgColorView]; // actually works!
}
return self;
}
which makes this even more weird.
Inspired by andykkt's comment, I found the explanation in awakeFromNib documentation:
The nib-loading infrastructure sends an awakeFromNib message to each
object recreated from a nib archive, but only after all the objects in
the archive have been loaded and initialized. When an object receives
an awakeFromNib message, it is guaranteed to have all its outlet and
action connections already established.
so that explains the weird behaviour above: initWithCoder instantiates a standard UITableViewCell (that already comes pre-baked with a backgroundView etc etc).. however it still doesn't recognize the outlets I've added to it via storyboard.. awakeFromNib does.
I suspect that initWithCoder: method doesn't draw the contents of the cell, it just, initialises the object, and as you have noticed, accessing any of the ui drawing methods of the cell actually 'draws' the cell to apply the changes you have made to the cell. You can see the same behaviour when initing UIViewController's using initWithNibName:bundle: method.
I'm trying to add a swipe gesture recognizer to a tableViewCell but it doesn't work.
This is the way I create my cell:
CellIdentifier = #"EventsSentCell";
nibObjcet = [[NSBundle mainBundle] loadNibNamed:#"EventsSentCell" owner:self options:nil];
EventsSentCell *cell = [[EventsSentCell alloc] init];
cell = (EventsSentCell *)[nibObjcet objectAtIndex:0];
and this is how my cell is initiated in the .m file:
-(id)init{
self = [super init];
if (self) {
leftSwipe = [[UISwipeGestureRecognizer alloc] init];
leftSwipe.direction= UISwipeGestureRecognizerDirectionLeft;
[leftSwipe addTarget:self action:#selector(swipedLeft)];
[self addGestureRecognizer:leftSwipe];
}
return self;
}
and this is how I declared my gesture recognizer in the .h file:
#property (nonatomic,strong) IBOutlet UISwipeGestureRecognizer *leftSwipe;
But for some reason my method isn't called.
Any ideas?
Thanks
I've tried putting the below code:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
NSLog(#"%#",gestureRecognizer);
return YES;
}
and the result i'm getting after swiping left is:
<UILongPressGestureRecognizer: 0xa9d99a0; state = Possible; view = <UITableViewCellContentView 0xa9d8ce0>; target= <(action=_longPressGestureRecognized:, target=<EventsSentCell 0xa9d8bb0>)>>
Before answering the actual question, let me point out some other issues in your code.
EventsSentCell *cell = [[EventsSentCell alloc] init];
cell = (EventsSentCell *)[nibObjcet objectAtIndex:0];
First of all, there's no point in these two lines. You're allocating and initializing an instance of EventSentCell without a nib. After doing this you're overwriting cell to point to the instance initialized by loadNibNamed:. You could simplify this to EventsSentCell = (EventsSentCell *)nibObject[0];
But even after these optimizations, this still isn't the recommended way to implement cellForRowAtIndexPath:. You should use registerNib:forCellReuseIdentifier: in viewDidLoad and then use
dequeueReusableCellWithIdentifier:forIndexPath: to get a cell and leave out loading the nib yourself completely.
Next,
#property (nonatomic,strong) IBOutlet UISwipeGestureRecognizer *leftSwipe;
You declare this property as an IBOutlet but you're setting it (as far as I know) only in code, more specifically the init method. You could just leave out the IBOutlet altogether.
And this init method is probably also the cause of your problem. When instantiating a view using loadNibNamed, initWithCoder: is called instead of init. Implement your custom initialization code (adding a gesture recognizer in this case) there and it should work just fine.
Your 'init' method is not getting called, so the gesture recognizer does not get set up.
You could try initialising in awakeFromNib instead, but anyway your cell creation looks unconventional.
Assuming you are using a custom cell class with a Xib file, here is how I would do it.
Create your EventsSentCell object .m and .h files
Create a xib file "EventsSentCell.xib"
In the xib file, delete the default top-level view and replace it with a UITableViewCell (you can drag one out from the objects library). In the identity inspector change it's class to EventsSentCell
In you table viewController's viewDidLoad...
UINib* EventsSentNib = [UINib nibWithNibName:#"EventsSentCell"
bundle:nil];
[self.tableView registerNib:EventsSentNib
forCellReuseIdentifier:#"EventsSentCell"];
In your cellForRowAtIndexPath method:
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"EventsSentCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"EventsSentCell"];
}
In EventsSentCell.m, trigger your initialisation code from -awakeFromNib
Your initialisation code:
leftSwipe = [[UISwipeGestureRecognizer alloc] init];
leftSwipe.direction= UISwipeGestureRecognizerDirectionLeft;
[leftSwipe addTarget:self action:#selector(swipedLeft)];
[self addGestureRecognizer:leftSwipe];
will work as it is.
You get the UILongPressGestureRecognizer:... response to your gestureRecogniser delegate method because that is a built-in gesture recognizer provided by Apple that has it's delegate set to the cell. When your code is working correctly, if you also set your gesture recongnizer's delegate to the cell (leftSwipe.delegate = self), you would expect to see a simlilar log for UISwipeGestureRecognizer:...
It is also noting that the UILongPressGestureRecognizer's view is NOT the cell, but the cell's contentView. This is the superview for your cell's view hierarchy, to which you should attach all of your cell's content. Although your gesture recognizer works when you attach it to the cell, I would advise following Apple here:
[self.contentView addGestureRecognizer:leftSwipe];
Then the gesture will correctly follow the cell's view hierarchy.
This topic has been covered over and over, but after two days of researching and trying all of the solutions suggested, I still can't do what I want.
First of all, I'm creating an app for iOS 5, using storyboard.
I have a UITableViewController, with 2 types of cell (an original "message", and a number of "answers" to it). I created my table in my storyboard, checked "prototypes cells", designed 2 cells with my 2 or 3 labels, a textView, and an image. Then, I subclassed UITableViewCell with 2 new classes, which I called ThreadAlertCell and ThreadAnswerCell. I created properties for my cell's elements, so I can set the text of the labels and the image programmatically. I linked my graphic elements to their definition in the storyboard as usual. In my TableViewController, in cellForRowAtIndexPath, I create the cells and populate them. So far so good, everything is displayed correctly and how I want it.
But, I want a "touch" on the image of a cell, to pop a new view, showing the user's profile page (an other basic view, I can do it with performSegue, no problem for that).
I have tried so many things I'm not sure it's very useful to put everything in detail here. While looking for answers, I understood that using a UIImageView when you expect to handle gestures is not really the best way. So I changed it to a UIButton. But I can't get the touch event to do anything !
I'll give only the example of an "answer" cell.
Here is my ThreadAnswerCell header file (I won't give the .m, nothing interesting there) :
#interface ThreadAnswerCell : UITableViewCell
#property (nonatomic) IBOutlet UILabel *senderLabel;
#property (nonatomic) IBOutlet UILabel *dateLabel;
#property (nonatomic) IBOutlet UITextView *contentTextView;
#property (nonatomic, weak) IBOutlet UIButton *senderButton;
#end
And here is half the cellForRowAtIndexPath from my TableViewController (I do the same for the "message" before that) :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"ThreadAnswerCell";
ThreadAnswerCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[ThreadAnswerCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
Message *message = [threadArray objectAtIndex:indexPath.row];
cell.senderLabel.text = [NSString stringWithFormat:#"%#", message.sender];
cell.contentTextView.text = [NSString stringWithFormat:#"%#", message.content];
cell.dateLabel.text = [NSString stringWithFormat:#"%#", message.date];
//[cell.senderButton setBackgroundImage: [[UIImage alloc] initWithData: [NSData dataWithBase64EncodedString: message.userPic]]
// forState: UIControlStateNormal];
[cell.senderButton addTarget:self action:#selector(firstButtonSelected:) forControlEvents:UIControlEventTouchUpInside];
CGRect frame = cell.contentTextView.frame;
frame.size.height = cell.contentTextView.contentSize.height;
cell.contentTextView.frame = frame;
[cell.contentTextView sizeToFit];
return cell;
}
As you can see, I use my custom cell, then populate it with content (I use three useless stringWithFormat but I have my reasons, lol), and I try to add an event to my button. I also commented the part where I set the button's background image to "see" the button on my screen.
And here is the method I want the buttons to call :
- (void)firstButtonSelected: (id)sender
{
NSLog(#"hello");
}
But, the method is never called ! Any ideas on where I've gone wrong or any other working solution would be great ! Thanks.
Do you have
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Implemented in your tableview delegate class?
I think, your cell catches the touch events. Try to delete the method. And
cell.selectionStyle = UITableViewCellSelectionStyleNone
Is it the topmost view? Make sure you add the senderButton view last to the cell view.
It's possible any other view gets touched.
I have a problem using iOS 5 new functionality to select multiple cells during editing mode.
The application structure is the following:
-> UIViewController
---> UITableView
----> CustomUITableViewCell
where UIViewController is both the delegate and the data source for the UITableView (I'm using an UIViewController instead of UITableViewController for requirement reasons and I cannot change it). Cells are loaded into the UITableView like the following code.
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableViewCell *cell = (CustomTableViewCell*)[tv dequeueReusableCellWithIdentifier:kCellTableIdentifier];
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"CustomTableViewCellXib" owner:self options:nil];
cell = self.customTableViewCellOutlet;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
// configure the cell with data
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
The cell interface has been created from a xib file. In particular, I've created a new xib file where the superview consists of a UITableViewCell element. To provide customization, I set the type for that element as CustomUITableViewCell, where CustomUITableViewCell extends UITableViewCell.
#interface CustomTableViewCell : UITableViewCell
// do stuff here
#end
The code works well. In the table custom cells are displayed. Now, during application execution I set allowsMultipleSelectionDuringEditing to YES and I enter the edit mode for the UITableView. It seems working. In fact, alongside of each cell an empty circle appears. The problem is that when I select a row the empty circle doesn't change its state. In theory, the circle has to change from empty to red checkmark and viceversa. It seems that circle remains above the contentView for the cell.
I've made a lot of experiments. I've also checked also that following method.
- (NSArray *)indexPathsForSelectedRows
and it gets updated during selection in editing mode. It displays the right selected cells.
The thing that it's not so clear to me is that when I try to work without a custom cell, only with UITableViewCell, the circle updates its state correctly.
Do you have any suggestion? Thank you in advance.
For those interested in, I've found a valid solution to fix the previous problem.
The problem is that when selection style is UITableViewCellSelectionStyleNone red checkmarks in editing mode aren't displayed correctly. The solution is to create a custom UITableViewCell and ovverride some methods. I'm using awakeFromNib because my cell has been created through xib. To reach the solution I've followed these two stackoverflow topics:
multi-select-table-view-cell-and-no-selection-style
uitableviewcell-how-to-prevent-blue-selection-background-w-o-borking-isselected
Here the code:
- (void)awakeFromNib
{
[super awakeFromNib];
self.backgroundView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"row_normal"]] autorelease];
self.selectedBackgroundView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"row_selected"]] autorelease];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
if(selected && !self.isEditing)
{
return;
}
[super setSelected:selected animated:animated];
}
- (void)setHighlighted: (BOOL)highlighted animated: (BOOL)animated
{
// don't highlight
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
}
Hope it helps.