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.
Related
UITableViewCells get emptied on scroll (Objective-C)
I am having a problem with UITableViewCells getting emptied as soon I a start to scroll within the table view.
I already had a look at Cells become empty after scrolling. (Xcode) - however the problem still persists.
1) I have a popover view controller, which presents a way to log into some administration. Upon successful login (which hasn’t been implemented yet, the LOGIN button simply takes one straight to a test tableView - which should be fed from some external database later on).
2) Upon successful login, the login view inside the popover gets removed and a custom UITableViewController comes into play with its own XIB.
3) This UITableViewController uses a custom UITableViewCell - since prototype cells are not possible within this configuration.
It all works to the point where I scroll the table - and all the cells get emptied for some reason.
Here is the code run down (I leave out the obvious, eg properties and table section, etc setups):
1) customPopUpViewController(XIB ,.h, .m):
- (IBAction)loginButtonPressed:(UIButton *)sender {
UITableViewController *libraryTableViewController = [[LibraryAdminTableViewController alloc]initWithNibName:#"LibraryAdminTableViewController" bundle:nil];
libraryTableViewController.view.frame = CGRectMake(0, 179, libraryTableViewController.view.frame.size.width, libraryTableViewController.view.frame.size.height);
[self.view addSubview:libraryTableViewController.view];
}
2) LibraryAdminTableViewController (XIB ,.h, .m):
- (void)viewDidLoad {
[super viewDidLoad];
self.LibraryAdminTable.delegate = self;
self.LibraryAdminTable.dataSource = self;
self.tblContentList = [[NSMutableArray alloc]init];
self.tblContentList = [NSMutableArray arrayWithObjects:#"Sync Pack 1",#"Sync Pack 2",#"Sync Pack 3", nil];
[self.LibraryAdminTable registerNib:[UINib nibWithNibName:#"LibraryAdminTableViewCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:#"LibraryAdminCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LibraryAdminTableViewCell *cell = [self.LibraryAdminTable dequeueReusableCellWithIdentifier:#"LibraryAdminCell" forIndexPath:indexPath];
NSString* trackList = [self.tblContentList objectAtIndex:indexPath.row];
cell.cellLabel.text = trackList;
return cell;
}
3) LibraryAdminTableViewCell (XIB ,.h, .m) - I gave the Identifier in the Attributes Inspector “LibraryAdminCell”:
#property (strong, nonatomic) IBOutlet UILabel *cellLabel;
What am I missing?
It is solved. According to this thread I had to get a strong reference to the custom UITableViewController via a property in the popup controller since the ViewController (being the DataSource for the tableView) would not be retained in memory.
This is happening because you are not creating a new cell, when tableview will try to dequeue a cell, and it does not get the cell, then it should create the cell to use but you are not creating any cell, so it is returning nil.Try the code below
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LibraryAdminTableViewCell *cell = (LibraryAdminTableViewCell*)[self.LibraryAdminTable dequeueReusableCellWithIdentifier:#"LibraryAdminCell"
forIndexPath:indexPath];
if (cell == nil)
{
cell = [[LibraryAdminTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"LibraryAdminCell"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
NSString* trackList = [self.tblContentList objectAtIndex:indexPath.row];
cell.cellLabel.text = trackList;
return cell;
}
I am having an interesting problem creating the custom tableview I need...
I found this question, but it does not really address my issue...
I am trying to put a subclassed UIView inside a subclassed UITableViewCell. The custom view holds a UIButton and a couple labels. Simplified, it's like this:
Both the custom view and custom tableviewcell have xibs.
MyCustomView.xib's class is set to MyCustomView and I have properties for the labels and the button as well as an IBAction for the button.
MyCustomTableView.xib has a property for MyCustomView and is importing MyCustomView.h.
In MyCustomView.xib, I have this init:
-(id)initWithNibName:(NSString *)nibName nibBundle:(NSBundle *)nibBundle myLabelText:(NSString *)labelText {
//The custom view in the tableviewcell is 69 by 64...
if ((self = [super initWithFrame:CGRectMake(0, 0, 69, 64)])) {
[self setmyLabelText:labelText];
}
return self;
}
And in my TableViewController...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomTableViewCell *cell = (MyCustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"theCell" forIndexPath:indexPath];
// Configure the cell...
cell.customView = [[MyCustomView alloc] initWithNibName:#"MyCustomView" nibBundle:nil fileContentID:#"Some Text..."];
return cell;
}
When I run the app, the custom tableview cell's contents are fine, but the content of the custom view inside the custom tableview cell is blank.
It seems that MyCustomView's initializer(-initWithNibName:nibBundle:myLabelText:) don't load any xib.
This post will help you.
How to load a xib file in a UIView
...and MyCustomView should be created once inside MyCustomTableViewCell, as #rdelmar says.
You need to do most of the formatting work in MyCustomTableViewCell - I would not use a XIB and code the views directly because that class is called many times. Apple has number of sample codes regarding TableViewCells - One of them I believe is called Elements that use fancy tableview cells for the Elements of the Periodic Table. Most of my apps use custom cells with icon images and I started with that sample code many years back (since IOS 4).
Your CellForRowatIndexPath should just be passing the image and the label text to your tableviewCell Class instance. If you have question just ask - but I am sure that sample code from apple is sufficient to get you started.
I have set a text for that cell but however, the text that it is shown is too long which affects the right detail text to be covered or not shown.
I can't change it as I need the name in the next viewcontroller. Is it possible to enable it to just display the text, followed by "...."?
EXAMPLE:
Electrical & Electronic Engi.... 01 >
LEGEND:
"Electrical & Electronic Engi...." as text displayed in the tableview, "01" as the detailTextLabel on the right and the ">" as the navigation.
This is how it should look like, http://oi58.tinypic.com/2j4vg5k.jpg, but due to some text is too long, this is what appears: http://oi58.tinypic.com/erc177.jpg
The textLabel and detailTextLabel doesn't seem to fit or show in the whole row. I would like the right detailTextLabel to be still there will the textLabel to end with a "...."
Thanks.
(I'm new to iOS Programming)
For this you will have to restrict the width of default textLabel of UITableViewCell or add new UILabel to cell.
you have two options
1)Dont use default textLabel of cell, create new UILabel and add it as a subview of tableview cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell using custom cell
//restrict width here while creating label (change 40 to what you want)
UILabel *tempLabel=[[UILabel alloc]initWithFrame:CGRectMake(0,0,40,20)];
tempLabel.text=#"The text you want to assign";
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[[cell contentView] addSubview:tempLabel];
}
return cell;
}
2)Or second way is to change width of default textLabel , for this you will have to create new subclass inheriting UITableViewCell, and in the subclass override method (void)layoutSubView and in that method change width(do it by trial and error method)
create new class with following .h and .m file
////CustomCell .h file
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell
#end
////CustomCell .m file
#import "CustomCell.h"
#implementation CustomCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
-(void)layoutSubviews{
[super layoutSubviews];
CGRect tempFrame=self.textLabel.frame;
//whatever you want to set
tempFrame.width=30;
self.textLabel.frame=tempFrame;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
Or one different option(better one)
3) Create custom tableview cell
custom tableview cell tutorial
And for having ... at the end of UILabel, there is property truncateTail of UILabel. you can use that.
Although the cell configuration is a bit different (only detail label and no disclosure indicator) this answer might be helpful: How can I truncate text in a UITableView Cell TextLabel so it doesn't hide the DetailTextLabel?
I am reading the apple docs on setting up custom subclasses of UITableViewCell - Docs
In this example I need to setup a custom cell which does not have a NIB/storyboard file. The apple docs provide an example of using a predefined style and configuring that but not creating a completely custom layout.
How should the cell be called in.. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath ?
I am looking to have a completely custom layout so is this correct? As the cell is being called initWithStyle...?
MESLeftMenuCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
cell = [[MESLeftMenuCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
In the custom cell subclass how/where should I implement the setup of the views within the contentView?
Which method is called for the init, would it be initWithStyle as above? If so, can I simply create the cell outlets in there once only?
Then in the cellForRowAtIndexPath can I access the outlets as i.e. cell.MainLabel.text ... ?
This is how I have been shown to set up my Collection View Cells in their custom class. I know you are using a tableview but this is threw me for a while so decided to add here. Hopefully it helps you.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)encoder
{
self = [super initWithCoder:encoder];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit
{
// set up your instance
}
To access the outlets of that cell I just add outlets to the header file of the custom class and you can easily access them.
As of iOS 6, you no longer need to check whether the dequeued cell is nil.
What you would do is as follows:
In the viewDidLoad method of the view controller containing the table view you could say
[self.tableView registerClass:[MyCellClass class] forCellReuseIdentifier:MyCellIdentifier];
This results in your dequeueReusablecellWithIdentifier call to never return nil. In essence, in the background, initWithStyle is called. So you would set your stuff up when overriding that function.
This is working fine for my plain style table views, but not for my grouped style. I'm trying to customize how the cell looks when it is selected.
Here is my code:
+ (void)customizeBackgroundForSelectedCell:(UITableViewCell *)cell {
UIImage *image = [UIImage imageNamed:#"ipad-list-item-selected.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
cell.selectedBackgroundView = imageView;
}
I have verified that the correct cell is indeed being passed into this function. What do I need to do differently to make this work?
It's not clear from your question whether or not you're aware that the tableViewCell automatically manages showing/hiding it's selectedBackgroundView based on its selection state. There are much better places to put that method other than in viewWillAppear. One would be at the time you initially create the tableViewCells, i.e.:
- (UITableViewCell *)tableView:(UITV*)tv cellForRowAtIP:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
cell = [tv dequeueCellWithIdentifier:#"SomeIdentifier"];
if (cell == nil) {
cell = /* alloc init the cell with the right reuse identifier*/;
[SomeClass customizeBackgroundForSelectedCell:cell];
}
return cell;
}
You only need to set the selectedBackgroundView property once in the lifetime of that cell. The cell will manage showing/hiding it when appropriate.
Another, cleaner, technique is to subclass UITableViewCell, and in the .m file for your subclass, override:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithBla....];
if (self) {
UIImageView *selectedBGImageView = /* create your selected image view */;
self.selectedBackgroundView = selectedBGImageView;
}
return self;
}
From then on out your cell should show it's custom selected background without any further modifications. It just works.
Furthermore, this method works better with the current recommended practice of registering table view cell classes with the table view in viewDidLoad: using the following UITableView method:
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier
You would use this method in your table view controller's viewDidLoad method, so that your table view cell dequeuing implementation is much shorter and easier to read:
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[SomeClass class]
forCellReuseIdentifier:#"Blah"];
}
- (UITableViewCell *)tableView:(UITV*)tv cellForRowAtIP:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"Blah"
forIndexPath:indexPath];
/* set your cell properties */
return cell;
}
This method is guaranteed to return a cell as long as you have registered a class with the #"Blah" identifier.