I have a tableview cell with a custom textview in it, I am now left wondering how do I possibly access the text in the textbox after text has been edited/added.
I would normally know how this is done when I draw the textfield through IB, but my textviewcell is dynamically drawn.
(i.e. I would like to capture the data that is updated in detailLabel.text)
Here is the related code to adapt to your answer.
Thanks again!
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
//Big Text Box
UITextView *detailLabel = [[UITextView alloc] initWithFrame:CGRectMake(30, 80, 500, 150)];
detailLabel.tag = 20;
[cell.contentView addSubview:detailLabel];
detailLabel.layer.borderWidth = 1;
detailLabel.layer.borderColor = [[UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.9] CGColor];
detailLabel.layer.cornerRadius = 10;
detailLabel.font = [UIFont fontWithName:#"Helvetica" size:17];
[detailLabel release];
}
UITextView * detailLabel = (UITextView *) [cell.contentView viewWithTag:20];
switch (indexPath.row) {
case 0:
detailLabel.text = #"no";
break;
default:
detailLabel.text = [NSString stringWithFormat:#"%d", indexPath.row];
detailLabel.hidden = NO;
}
You'll want to listen for messages sent by the UITextView. So look to implement the UITextViewDelegate protocol and register the implementing class with the detailLabel using its delegate property.
When your delegate is notified of a change in the UITextView you'll have to identify the cell which currently owns the UITextView. In doing so you must remember that cells can be reused.
I would start by looking at the following method in the UITableView class:
- (NSArray *)visibleCells
We know that if the user has made a change to the UITextView's contents it must currently be on screen and therefore be present in the pre-mentioned array. To find it we use the pointer to the UITextView which changed (it's a parameter in the textViewDidChange protocol method). So simply iterate over the visibleCells array and retrieve the UITextView and compare it against the UITextView which changed.
- (void)textViewDidChange:(UITextView *)textView {
....
cellsLabel = (UITextView *) [cell.contentView viewWithTag:20];
if (cellsLabel == textView)
...
You'll now have a handle to a cell in the UITableView. To find the index use the following UITableView method:
- (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell
Related
I want to have my text in my UITableViewCell to be a little bit to the right. In other words I want to have an x-offset. Is this even possble? Do I have to create a custom cell just because of this?
You could use cell.indentationLevel and if needed cell.indentationWidth instead of custom UITableViewCell for this.
You can try this :
[cell setIndentationLevel:SOME_NUMBER];
[cell setIndentationWidth:SOME_OTHER_NUMBER];
A simple solution is you can change the frame of the textLabel.
CGRect textLabelFrame = cell.textLabel.frame;
textLabelFrame.origin.x += xOffset;
textLabelFrame.size.width -= xOffset;
cell.textLabel.frame = textLabelFrame;
I've also done this by creating a custom UILabel that supports edgeInsets similar to a UIButton. This is a better solution b/c you can layout the label to correct size but the above will work if you have simple needs.
[EDIT 1/2: fixed typo w/ CGRect]
[EDIT 3: fixed typo setting modified frame]
[EDIT 4: need a simple subclass]
Mea culpa. I was wrong that you can do this in tableView:cellForRowAtIndexPath. The UITableViewCell layout happens after the tableView delegate/datasource has a chance to customize the cell. I've tested the implementation below, it works.
Do as I say above but create a (simple) subclass of UITableViewCell that adds the xOffset in layoutSubviews. If you do this, you can also add an xOffset property that you can set in tableView:cellForRowAtIndexPath:.
#implementation XOffsetCell
// assumes property xOffset is defined and synthesized
- (void) layoutSubviews
{
[super layoutSubviews];
CGRect textLabelFrame = cell.textLabel.frame;
textLabelFrame.origin.x += self.xOffset;
textLabelFrame.size.width -= self.xOffset;
cell.textLabel.frame = textLabelFrame;
}
#end
The solution that suggested adding a custom UILabel to cell.contentView is also a good solution. I saw your comment that it obscures the built-in textLabel but that's the whole point. You would not use the built-in label anymore, use the custom label instead.
I'm not trolling for votes, but wanted to show what the code provided by #iPhone monster "should" look like. His solution is a valid option. If you add the label to the cell after the if (cell == nil) as he did you will be continually adding labels to dequeued cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(50.0f, 10.0f, 150.0f, 20.0f)];
lbl.tag = OffsetLabelTag; // define this as a constant
[cell.contentView addSubview:lbl];
[lbl release];
}
UILabel *lbl = (UILabel *)[cell.contentView viewWithTag:OffsetLabelTag];
[lbl setText:#"test text"];
return cell;
}
You can do that with Autolayouts:
UITableViewCell *cell = [UITableViewCell new];
cell.textLabel.translatesAutoresizingMaskIntoConstraints = NO;
[cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(indentation)-[textLabel]-(indentation)-|"
options:0
metrics:#{#"indentation": #35}
views:#{#"textLabel": cell.textLabel}]];
Or using Parus lib:
[cell.contentView addConstraints:(PVVFL(#"H:|-(indentation)-[textLabel]-(indentation)-|")
.withViews(#{#"textLabel": textLabel})
.metrics(#{#"indentation": #35}).asArray)];
Other way:
- (NSInteger)tableView:(UITableView *)tableView
indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
return intOffset; // but is like '\t', not points
}
There is another not very pretty and smart way : ) Position of Label in TableViewCell depends on size of image assigned to cell.imageView.image property. So if you want your text to move right you can add several blank pixel columns to image in cell.
Based off of https://stackoverflow.com/a/5659826/1058199, here is a Swift version.
This works within the cell to ONLY move the textLabel to the right. Swift 5.4
override func layoutSubviews() {
super.layoutSubviews()
var textLabelFrame = self.textLabel?.frame
textLabelFrame?.origin.x += 32
textLabelFrame?.size.width -= 32
self.textLabel?.frame = textLabelFrame ?? CGRect.zero
}
Will cell.textLabel.textAlignment = UITextAlignmentCenter; be enough for you. It could align the text to the center creating that space for you
I am adding my own UILabels to the contentView of a UITableViewCell because I need more control over the layout than the default UITableViewCellStyles provide. In essence I want the detailLabel have priority over the textLabel so the textLabel gets truncated.
I have the following code in my UITableViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * const kCellIdentifier = #"CustomCell";
UITableViewCell * cell =
[tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
UILabel * titleLabel, * dateLabel;
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:kCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
titleLabel = [[[UILabel alloc] init] autorelease];
titleLabel.tag = kTitleLabelTag;
titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
dateLabel = [[[UILabel alloc] init] autorelease];
dateLabel.tag = kDateLabelTag;
dateLabel.textColor = [UIColor blueColor];
dateLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
[cell.contentView addSubview:titleLabel];
[cell.contentView addSubview:dateLabel];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)pCell
atIndexPath:(NSIndexPath *)pIndexPath
{
const float kHeight = 44.0, kLeftIndent = 8.0, kOverallWidth = 293.0,
kGap = 1.0;
UILabel * titleLabel, * dateLabel;
titleLabel = (UILabel *)[pCell.contentView viewWithTag:kTitleLabelTag];
dateLabel = (UILabel *)[pCell.contentView viewWithTag:kDateLabelTag];
NSString * dateText = #"9:39 AM";
// Calculate the size of dateLabel
CGSize dateSize = [dateText sizeWithFont:[dateLabel font]
constrainedToSize:CGSizeMake(kOverallWidth, kHeight)];
const float dateXPos = kOverallWidth - dateSize.width;
dateLabel.frame = CGRectMake(dateXPos, 0.0, dateSize.width, kHeight);
titleLabel.frame = CGRectMake(kLeftIndent, 0.0,
dateXPos - kLeftIndent - kGap, kHeight);
titleLabel.text = #"Some potentially very long text which will be wrapped.";
dateLabel.text = dateText;
pCell.contentView.backgroundColor = [UIColor purpleColor];
}
The code above produces incorrect results. When the table view is initially shown, it looks like figure 1) in this image of the renderings.
So there is a unwanted gap on the right of all the dateLabels. (the purple background is just for better visibility what is going on)
When dragging the tableview up like in 2) in the image, it then bounces back and looks like 3).
The first row has now exactly the layout I wanted and which calculated in configureCell:atIndexPath:. I guess this behavior happens because the cells get re-used and then configured again.
So it feels like I am missing some kind of initialization, I have tried calling setNeedsLayout and layoutSubviews of pCell and pCell.contentView but never achieved an initial correct rendering.
Only when I set the autoresizingMask of titleLabel and dateLabel to UIViewAutoresizingNone I get a correct initial rendering, then however the swipe to delete does not work because the delete button gets rendered over the dateLabel.
What do I have to change in my code so that all cells get rendered initially like the first cell in the third picture?
Thanks!
PS: I would like to have inlined the pictures but unfortunately I do not have enough reputation for that.
A good, and maybe simpler way to do this is to:
First Create a custom UITableViewCell subclass which you can setup using Interface Builder. If "MyTableViewCell" is your custom cell view you initialize it in CellForRowAtIndexPath like this:
MyTableViewCellClass *cell = (MyTableViewCellClass *)[tableView dequeueReusableCellWithIdentifier:#"MyTableViewCellClass"] autorelease];
if (!cell)
cell = [[[MyTableViewCellClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyTableViewCellClass"] autorelease];
// Call specific methods on your cell to pass information to it, not for display
[cell setProperties:...];
Then implement the layoutSubviews method in your custom UITableViewCell subclass. For instance:
-(void) layoutSubviews
{
// You must call this first to make sure your cell gets current parent information
[super layoutSubviews];
// Retrieve the content view bounds. This will include the edit symbols when present (delete button and ordering symbol
float inset = 5.0;
CGRect bounds = [[self contentView] bounds];
// Keep on going here with your own view layout.
}
Doing this you basically separate the cell model (CellForRowAtIndexPath) from the cell view (your custom implementation of cell drawing). If you later change the implementation (layout) of your cell you can do this easily simply by changing your cell layout without worrying about the CellForRowAtIndexPath method.
If you work with a storyboard , you should disable the "Use Auto Layout" in the File Inspector!
Then set the cell's property autoresizesSubviews
- (void)awakeFromNib
{
// Initialization code
self.autoresizesSubviews=YES;
}
The final step is configure the frame of the Label , overriding the method layoutSubviews of the UITableViewCell class
-(void)layoutSubviews{
[super layoutSubviews];
CGFloat top=VerticalPadding;
CGFloat left=HorizentalPadding;
CGFloat width=CGRectGetWidth(self.frame)-2*HorizentalPadding;
CGFloat height=CGRectGetHeight(self.frame)-2*VerticalPadding;
CGRect rect=CGRectMake(left, top, width, height);
self.Label.frame=rect;
}
I have been looking for a solution to this issue for a few days now... and cannot find someone with a similar problem or a solution that would work for me. At this point I am not even sure that I am doing something wrong, as I have read and analyzed many sample code and I am almost 100% sure that I am doing this the way it should...
Anyway here it comes:
I have a UITableView to which I display custom built UITableViewCell, here is where I create them:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
NSDictionary *myDictionary = [myArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier: CellIdentifier] autorelease];
CGRect textViewRect = CGRectMake(10.0, 0.0, 250, 25);
UILabel *textView = [[UILabel alloc] initWithFrame:textViewRect];
textView.text = [myDictionary objectForKey:#"name"];
textView.font = [UIFont systemFontOfSize:12.0];
textView.textColor = [UIColor blackColor];
textView.highlightedTextColor = [UIColor whiteColor];
textView.textAlignment = UITextAlignmentRight;
[cell.contentView addSubview:textView];
[textView release];
CGRect imageRect = CGRectMake(270.0, 15, 16, 16);
UIImageView *icon = [[UIImageView alloc] initWithFrame:imageRect];
icon.image = [myDictionary objectForKey:#"color-icon"];
icon.highlightedImage = [myDictionary objectForKey:#"gray-icon"];
[cell.contentView addSubview:icon];
[icon release];
}
So as you can see, pretty standard stuff... Then when I click on one of the cell, another view gets loaded instead of the table.
Until now, everything is fine, but then when I come back to that table view and that it has to reload the problem starts...
By the way I have added this to the delegate methods so the cells never stay selected:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// [...] a lot more code here
}
When I clicked on my cell in the simulator with the mouse and keep it down the cell stays selected as expected (same way on the device itself) and this is why I get:
[Image 1]
The UILabel Text is totally mixed up ! The screenshot is actually of the last cell of the table and the overlapping text is the one of the first cell. And the behavior on the first is similar, if I maintain the click on it, it will show the same behavior mixing up with the UILabel text of the last cell. I am guessing that is is due to the way the dequeueReusableCellWithIdentifier works (probably a FILA queue).
Of course I tried to work around that and found some really weird stuff.
If I don't click the cells are perfect, no bugs, the correct text is displayed etc:
[Image 2]
Then I tried to mess up a little bit with the parameters of my UILabel. I added this:
textView.backgroundColor = [UIColor clearColor];
And when I do this, as soon as the table reload, then I don't even need to highlight the cell to see the screw up behavior:
[Image 3]
The only way I was about to get rid of the problem to always instantiate a new cell rather than dequeueing one...
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier: CellIdentifier] autorelease];
was replaced by:
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier: CellIdentifier] autorelease];
But of course this won't work on the long term because of the memory leak...
Also, it is to be noted that I have the exact same behavior for another UITableView in another view somewhere in this App... The other custom cell is more complex, more labels, more images, etc, but all the label exhibit the same behavior. And this more complex Table View is managed by a Navigation Controller, so no custom loading / unloading like the first one...
That's all I have, and I can't find a solution... please help !
Arghh this is really annoying... I am new so I cannot post images... :(
Here is a link where you can see the referenced images: https://skitch.com/aponsin/rne9k/fullscreen.png-100-layer-3-rgb-8
Alex
The problem is that you create new label and add it to cell each time cell is being reused. To fix that you must create your label only when your cell is created:
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier: CellIdentifier] autorelease];
CGRect textViewRect = CGRectMake(10.0, 0.0, 250, 25);
UILabel *textView = [[UILabel alloc] initWithFrame:textViewRect];
textView.tag = kLabelTag;
textView.font = [UIFont systemFontOfSize:12.0];
textView.textColor = [UIColor blackColor];
textView.highlightedTextColor = [UIColor whiteColor];
textView.textAlignment = UITextAlignmentRight;
[cell.contentView addSubview:textView];
[textView release];
CGRect imageRect = CGRectMake(270.0, 15, 16, 16);
UIImageView *icon = [[UIImageView alloc] initWithFrame:imageRect];
icon.image = [myDictionary objectForKey:#"color-icon"];
icon.highlightedImage = [myDictionary objectForKey:#"gray-icon"];
[cell.contentView addSubview:icon];
[icon release];
}
And after that you can get your label from cell using tag property you've assigned:
UILabel *textView = (UILabel*)[cell.contentView viewWithTag:kLabelTag];
textView.text = [myDictionary objectForKey:#"name"];
same logic also applies to setting up your icon imageview if it varies in different cells
Also have a look at components standard UITableViewCell already provides (depending on its cell style) - there's already UIImageView and UILabels there and you can set your custom properties to them and use them without creating extra components
In my iPhone app, I have a table view.
I want that when the user selects (or basically touches) on the cell of the tableview then he/she should be able to edit the cell contents.
How can I do that?
#PARTH in order to make your UITableView cells editable i suggest u to give textfields to your cell....
eg:- In
-(UITableViewCell *)reuseTableViewCellWithIdentifier:(NSString *)identifier withIndexPath:(NSIndexPath *)indexPath {
CGRect cellRectangle = CGRectMake (0, 10, 300, 70);
CGRect Field1Frame = CGRectMake (10, 10, 290, 70);
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:cellRectangle reuseIdentifier:identifier] autorelease];
UITextField *textField;
//Initialize Label with tag 1.
textField = [[UITextField alloc] initWithFrame:Field1Frame];
textField.tag = 1;
[cell.contentView addSubview:textField];
[textField release];
return cell;
}
and in your cellForRowAtIndexPath method // Configure the cell.
UITextField *txtTemp = (UITextField *)[cell.contentView viewWithTag:1];
use this where u want in your tableview accordingly otherwise hide it.....Hope it will help u!!
you need to put text field inside the table cell use this link.
I've got an annoying problem in my tableview. I was able to achive color-changing cells (blue/white/blue/...), but now I'm in trouble with my text, which has a white background on the blue cells.
I've tried for testing to set a background color to red:
// try to set the backgroundcolor of the text ???
cell.textLabel.text.backgroundColor = [UIColor redColor];
which doesn't work; hmph.
Please have a look at my code below; can anybody tell me what's wrong and how I can solve my problem by giving the text a transparent background?
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell
cell.textLabel.text=[RssFeedNodes objectAtIndex:indexPath.row];
// try to set the backgroundcolor of the text ???
cell.textLabel.text.backgroundColor = [UIColor redColor];
// show image in cell
NSString *imageName=#"rss.png";
cell.imageView.image=[UIImage imageNamed:imageName];
// changing colors in cells
NSInteger row = [indexPath row];
if (row % 2){
cell.contentView.backgroundColor = [UIColor whiteColor];
}
else {
cell.contentView.backgroundColor = [UIColor colorWithRed:0.90f green:0.95f blue:1.0f alpha:1.0f];
}
return cell;
}
Your answer is found (and described very well) here: http://undefinedvalue.com/2009/11/02/easy-gradient-backgrounds-uitextviewcells
My brief summary of the solution: Subclass the UITableViewCell, and then use your subclass when creating instances in cellForRowAtIndexPath.
You then need to override just one method (setSelected) in your subclass UITableViewCell:
(void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
for (UIView *view in self.contentView.subviews) {
view.backgroundColor = [UIColor clearColor];
}
}
The reason appears to be that the built-in UITableViewCell class will set the label background to white (or selected color) when being displayed based on the selection state in the table in the setSelected method. Substitute your own, call the base class implementation, then set your subview backgrounds to clear in order to let your contentView background shine through.
text has no background, but textLabel has. so
[[cell textLabel] setBackground:[UIColor redColor]];
You'll need to add your own UILabel label onto the cell, and set the background colour of that to transparent. For some reason the label that a table cell has does not have a settable background colour.
Something like:
UILabel* label = [[UILabel alloc] init];
label.frame = CGRectMake( 20, 10, 200, 22 );
label.backgroundColor = [UIColor clearColor];
label.opaque = NO;
label.text = #"your text here:";
[cell addSubview:label];
[label release];
In this example I've set the frame for the label fairly arbitrarily (well not, actually, this was modified from some of my own real code). You may need to be more dynamic with the sizing, in which case you'll probably need to subclass the cell and override setFrame to keep the label's frame in sync. But hardcoded values should get you going for now.
#define LABEL_TAG 99
// whatever your label rect size should be... change as appropriate
UIlabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300.0, 30.0)];
label.tag = LABEL_TAG;
// set up alignment, font, autoresizemask, etc.
label.backgroundColor = [UIColor clearColor];
label.opaque = NO;
[cell.contentView addSubview:label];
[label release];
Something to watch out for is that you're not adding your own label to a reuseable table cell more than once. By setting a known view.tag property you can get at (or discover existence of) the UILabel view. [cell viewWithTag:LABEL_TAG]
Whenever you dequeue a reuseable cell, first get the reference to your label view, then do what you would normally do if you were using the UITableCell's textLabel.