I have a UITableView filled with cells from a NIB-based subclass of UITableViewCell. I obtain each one like this:
+(id) getClassObjectFromNib:(NSString*) nibName subclassOf: (Class) cls owner:(id)own
{
id result = nil;
NSArray* topLevelObjects = [[NSBundle mainBundle]
loadNibNamed:nibName
owner:own
options:nil];
for ( id currentObject in topLevelObjects )
{
if ([currentObject isKindOfClass:cls])
{
result = currentObject;
[result retain];
break;
}
}
return result;
}
My call looks like:
#interface TargetViewController : UITableViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [TargetCell defaultReuseIdentifier];
TargetCell* cell = (TargetCell*) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = (TargetCell*) [UtilityHelper getClassObjectFromNib:CellIdentifier subclassOf:[UITableViewCell class] owner:self];
}
if ( nil != cell )
{
// Other initialization code for cell controls
cell.showsReorderControl = 1;
}
return cell;
}
But 'dealloc' never gets called on the cells when their view unloads. If I remove the 'retain' above, dealloc gets called, but the app crashes when a cell is deleted individually (via swipe) from the UITableView (crash due to message to deleted item).
Except for the single deletion case, releasing the items occurs property when the view unloads. The crash is "-[TargetCell _setDeleteAnimationInProgress:]: message sent to deallocated instance".
I discovered the problem was due to reloading the table view as part of the row deletion operation (in commitEditingStyle).
[self.tableView reloadData];
This appears to work for standard UITableViewCells but does not work when the cell is a subclass loaded from an XIB. Now I just alter the data source, delete the row (using deleteRowsAtIndexPaths) and return.
Related
First of All Thanks for the iCarousel. I am trying the iCarousel in custom TableView Cell, but it crashes by saying "Unrecognized selector sent in numberOfItemsInCarousel". Please help me on this. Thanks in advance..!
-(nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
TimelineCell *cell = (TimelineCell *)[tableView dequeueReusableCellWithIdentifier:#"timelineCell"];
if (cell == nil) {
NSArray *cellItem = [[NSBundle mainBundle]loadNibNamed:#"TimelineCell" owner:self options:nil];
cell = [cellItem firstObject];
}
cell.images = imageArray;
return cell;
}
- (NSInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
[carousel setType:iCarouselTypeTimeMachine];
return [_images count];
}
Sounds like the delegate for the iCarousel is set to something else than the class implementing the method numberOfItemsInCarousel:(….
Make sure you've set the delegate to the correct instance.
You probably want the TimelineCell to be the carousel delegate, so make sure that you do set it (either in your xib-file or programatically). Put the numberOfItemsInCarousel(…-method implementation in the TimelineCell class and it should work.
Another tip is to not set the carousel type in the method numberOfItemsInCarousel(…. Not a pretty place for it. Instead do it in some initialisation method.
After getting the data from multiple device, I am reloading tableview but tableview is flashing the labels. For example, two rows are in tableview contains two labels each. When I call reload tableview, displaying data in first row and second row will be empty and when displaying second row, first row will be empty. Like that it is flashing please help me how can I solve this
like this I am reloading tableview
[devicesTableView performSelectorOnMainThread:#selector(reloadData)
withObject:nil
waitUntilDone:NO];
This is CellForAtIndexPath Method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"DeviceCustomTableViewCellIdentifier";
devicesCustomTableViewCell = (DeviceCustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (devicesCustomTableViewCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DeviceCustomTableViewCell" owner:self options:nil];
devicesCustomTableViewCell = [nib objectAtIndex:0];
}
DeviceDetails *deviceDetailsEntity = [devicesArray objectAtIndex:indexPath.row];
devicesCustomTableViewCell.deviceName.text = deviceDetailsEntity.deviceLocalName;
for (int i=0; i<[dataArray count]; i++) {
DeviceParticulars *deviceParticulars = [dataArray objectAtIndex:i];
if ([[[deviceParticulars.peripheral identifier] UUIDString] isEqualToString:deviceDetailsEntity.deviceAddress]) {
devicesCustomTableViewCell.temperatureValueLabel.text = deviceParticulars.tempReadOutStr;
}
In this, DeviceDetails class is core data class, In that I am saving BLE device name as per requirement.
DeviceParticulars class is NSObject class for saving data from multiple BLE devices like I am getting temperature from multiple devices. I am displaying Temp values in tableview.
dataArray is an array contains DeviceParticulars object.
Reloading the entire table every time a peripheral value changes is expensive and, as you are seeing, has visual impacts.
You can change your custom cell to act as a delegate to your model object - DeviceParticulars
In your DeviceParticulars.h file, register the delegate property and protocol
#property (weak,nonatomic) id delegate;
#protocol DeviceParticularsDelegate
- (void)didUpdateDevice:(DeviceParticulars *)device;
#end
In your DeviceParticulars.m file, where you update readings, call
[self.delegate didUpdateDevice:self];
Then in your DeviceCustomTableViewCell.h, add <DeviceParticularsDelegate> to the class definition and add a property to store your deviceParticulars
#property (strong,nonatomic) DeviceParticulars *myDevice;
In the .m implement the delegate method
-(void)didUpdateDevice:(DeviceParticulars *)device {
// Update cell labels as required
}
and implement prepareForReuse
- (void)prepareForReuse {
self.myDevice.delegate=nil; // Remove this cell as delegate for existing device;
}
Finally, set the delegate in your cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"DeviceCustomTableViewCellIdentifier";
devicesCustomTableViewCell = (DeviceCustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (devicesCustomTableViewCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DeviceCustomTableViewCell" owner:self options:nil];
devicesCustomTableViewCell = [nib objectAtIndex:0];
}
DeviceDetails *deviceDetailsEntity = [devicesArray objectAtIndex:indexPath.row];
devicesCustomTableViewCell.deviceName.text = deviceDetailsEntity.deviceLocalName;
for (int i=0; i<[dataArray count]; i++) {
DeviceParticulars *deviceParticulars = [dataArray objectAtIndex:i];
if ([[[deviceParticulars.peripheral identifier] UUIDString] isEqualToString:deviceDetailsEntity.deviceAddress]) {
deviceParticulars.delegate=deviceCustomTableViewCell; //Set the delegate
devicesCustomTableViewCell.myDevice=deviceDetails; //Set device property
devicesCustomTableViewCell.temperatureValueLabel.text = deviceParticulars.tempReadOutStr;
}
Try using the -dequeueReusableCellWithIdentifier:forIndexPath: method for iOS 6 and later. It will automatically instantiate a cell if there is no reusable one, so you don't have to check if cell==nil. Not sure what causes your problems but I think it's worth to try it.
Please let me know if it helps.
I'm newbie in IOS and again i face another issue. How can i prevent data vanish from a table cell when i scroll a tableview.
I'm using the code below to load data on the table...Works fine but the data disappear when table cell go in not visible to the screen.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
list = [self.listas objectAtIndex:[indexPath row]];
static NSString *CellIdentifier = #"drop";
item_drop *cell = (item_drop*) [tabela_listas dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"item_drop" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.texto_drop.text = list.nome_lista;
return cell;
}
In android i used a holder to do it. There is anything similiar on IOS?
Since you are using reusable cells of a custom subclass of UITableViewCell, make sure you register the cell identifier in the UITableView, associating it to your custom cell type. i.e:
[yourTableView registerClass:[item_drop class] forCellReuseIdentifier:#"drop"];
You typically do this when you configure subviews in the UIViewController that controls the view your UITableView is a part of, in viewDidLoad.
With that in place, you should never hit the code inside if (cell == nil).
I have a problem with my cell textfield values when scrolling on a UITableView. When I scroll down and hide a custom cell, the value of the textField is deleted. The dequeueReusableCellWithIdentifier method doesn't work. I have this:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SectionsTableIdentifier = #"MyCustomCell";
MyCustomCell *cell = (MyCustomCell *) [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if (cell == nil) {
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:self options:nil];
cell = [objects objectAtIndex:0];
}
cell.labelCustomAttribute.text= #"Attribute Name";
cell.textFieldCustomAttribute.delegate = self;
return cell;
}
I find it easier to register the custom cell with the tableView in the viewDidLoad method and then simply use dequeueReusableCellWithIdentifier. If you register the cell, the dequeue method will automatically pick up a reusable cell OR allocate a new custom cell (if none is available).
Example:
-(void)viewDidLoad
{
[super viewDidLoad];
// Get a point to the customized table view cell for MyCustomCell
UINib *myCustomCellNib = [UINib nibWithNibName:#"MyCustomCell" bundle:nil];
// Register the MyCustomCell with tableview
[[self tableView] registerNib:myCustomCellNib forCellReuseIdentifier:#"MyCustomCell"];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SectionsTableIdentifier = #"MyCustomCell";
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
cell.labelCustomAttribute.text= #"Attribute Name";
cell.textFieldCustomAttribute.delegate = self;
return cell;
}
Normally the reuseIdentifier is assigned in the UITableViewCell's initWithStyle:reuseIdentifier: method, which you are not using because you are loading your view from a Nib.
You cannot set this property after because it is read only.
Maybe you can try instanciating the cell using the standard initWithStyle:reuseIdentifier: and add the view from your Nib as a subview of the cell's ContentView...
Now what is happening in your case is that you create a new cell every time that the Table View needs to display one. Clearly, this is not going to work. Actually, if you were reusing cells, you would have to also store the content of your text field somewhere (preferably in your data source) and put it when you reuse the cell. If you do not store it, when the cell is going to be reused, it will contain the data from the previous row in which it was displayed.
i have a uitableview in the uiviewcontroller, i made a scrollview in the viewload event.
i am adding it to tableview's first cell. but i scroll the tableview it displays more than one scrollview after 5 cell passed.
here is the code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"customCell";
DetailCellViewController *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nibObjects =[[NSBundle mainBundle] loadNibNamed:#"DetailCellView" owner:nil options:nil];
for (id currentObject in nibObjects)
{
if ([currentObject isKindOfClass:[DetailCellViewController class]])
{
cell = (DetailCellViewController *) currentObject;
}
}
}
if (indexPath.row==0) {
[cell.contentView addSubview:scrollView];
}
else {
NSMutableDictionary *dictionary=[catData objectAtIndex:indexPath.row-1];
NSString *title =[dictionary objectForKey:#"title"]];
[cell.catTitle setText:title];
}
return cell;
}
in which event should i add & remove scrollview?
My guess is that you're getting a dequeued UITableViewCell that already contains the UIScrollView. If you really care about separating cell types, I'd recommend setting it up so that you at least have two CellIdentifier strings. (There are times where I've set up a UITableView to handle 4+ different cell types; once you go beyond one cell type, it's pretty much just more of the same.)
My suggested solution: (see explanation below code)
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"bodyCell";
static NSString *HeaderIdentifier = #"headerCell";
UITableViewCell *cell;
// I break this up into 3 sections
// #1. Try to dequeue a cell
// #2. Create a new cell (if needed)
// #3. Set up the cell I've created
// Step 1: Try to dequeue a cell
if ([indexPath section] == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:HeaderIdentifier];
} else {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
// At this point, we may or may not have a cell to use,
// so we check for the cell's value being equal to 'nil'
// and create a new cell if it is
// Step 2: Create a new cell (if needed)
if (cell == nil) {
// Again, here we check for section to determine
// what kind of cell we want
if ([indexPath section] == 0) {
// We have the "header"/first cell
// Option 1
cell = [[ScrollViewTableViewCell alloc] init];
// Option 2 (this assumes you've got a xib named
// ScrollingTableViewCell along with a class property
// named headerCell and have properly wired it up in
// Interface Builder)
[[NSBundle mainBundle] loadNibNamed:#"ScrollingTableViewCell"
owner:self
options:nil];
cell = [self headerCell];
[self setHeaderCell:nil];
} else {
// We have a "body" cell (anything other than the first cell)
// Option 1
cell = [[BodyTableViewCell alloc] init];
// Option 2 (again, assuming you've set things up properly)
[[NSBundle mainBundle] loadNibNamed:#"BodyTableViewCell"
owner:self
options:nil];
cell = [self bodyCell];
[self setBodyCell:nil];
}
}
// At this point, whether dequeued or created
// new, 'cell' should be populated
// Again, we check for section and set up the cell as appropriate
if ([indexPath section] == 0) {
// Set up the header (UIScrollView) cell as appropriate
// This is where you would add the UISCrollView to your cell
// (if you haven't set up the UIScrollView through Interface Builder)
} else {
// Set up the "body" cell as appropriate
}
return cell;
}
NOTE: I HIGHLY recommend using Option 2 above. By far, the best results I've found when using custom/non-standard UITableViewCells is to make my own UITableViewCell subclass and xib to go along with it. Here are the steps for that:
Create a subclass of UITableViewCell (we'll call yours ScrollingTableViewCell.h/.m)
Class forward/import ScrollingTableViewCell into your UITableViewController (or UIViewController that's hosting a UITableView).
Create a class property of type ScrollingTableViewCell (we'll call yours ScrollingCell).
Create a View (New File > User Interface > View) (we'll call yours ScrollingTableViewCell.xib).
Delete the stock view item in the xib and replace it with a UITableViewCell item.
Alternative #4/5
Create an empty Xib.
Add a UITableViewCell item.
VERY IMPORTANT
In the xib, the File's Owner is the ViewController, NOT the UITableViewCell. The cell's class is ScrollingTableViewCell.
In IB, connect the ViewController's ScrollingCell property to the UITableViewCell item.
If you follow the above instructions, you should be able to allocate your cell using Option 2 above and then you can set up your cell in ScrollingTableViewCell.h/.m.