DTGridView doesn't interact with UILabel - iphone

I am using DTGridView with a subclass of DTGridViewCell with a UILabel and UITextField to do an in-place cell editing. That idea worked for me in UITableView like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
EditableDetailCell *cell = (EditableDetailCell *)[tableView cellForRowAtIndexPath:indexPath];
[[cell textField] setFrame:CGRectMake(8, 0, cell.frame.size.width, cell.frame.size.height)];
[[cell textField] becomeFirstResponder];
[[cell mylabel] setText: nil];
}
The text disappear and the textfield appears. The same thing doesn't work for me with
- (void)gridView:(DTGridView *)agridView selectionMadeAtRow:(NSInteger)rowIndex
column:(NSInteger)columnIndex
{
cell textField] setFrame:CGRectMake(8, 0, cell.frame.size.width, cell.frame.size.height)];
[[cell textField] becomeFirstResponder];
[[cell label] setText: nil];
}
Can anyone please help me? Thanks a lot.

There was a little problem in my question. For the second code I did
MyCustomCell* cell = (MyCustomCell *)[aGridView cellInfoForRow:rowIndex column:columnIndex];
before i make cell changes. That works and respondos to my first question. But now I have a second problem. If I scroll off the selected cell and later came it back to show, then the reused cell shows the content with some problem. Example: textField has not become first responder any more, cell is not selected, and the custom cell is drawn in a different cell. How can I solve that problems?

Related

UITableViewCell randomly disappears + scrolling performance issue on iPhone 4

To sum it up, I'm facing 2 problems, the 1st is that time to time some cells disappear from the tableview and reappear randomly. The 2nd is a scrolling performance issue on iPhone 4 and below.
1st problem :
I am displaying a simple list of elements in an UITableView by adding a subview the [cell contentView]. Most of the time it works well, but sometimes when i scroll (and really randomly) a cell disappears.
Because this cell which "disappeared" is reused, the reused cell will also be "blank" when I scroll. And randomly again a cell can reappear/disappear while scrolling.
Here is my code for the cellForRowAtIndexPath: method :
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
LBSelectorChampionViewController *item = [self itemAtIndexPath:indexPath];
if (cell == nil) {
NSLog(#"CELL NIL");
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[[item view] setTag:99];
[[item champName] setTag:50];
[[item champImage] setTag:51];
[[item buttonFavorite] setTag:52];
[[cell contentView] addSubview:[item view]];
}
UILabel *championLabel = (UILabel*)[[[cell contentView] viewWithTag:99] viewWithTag:50];
LBFavoriteChampionButton *buttonFavorite = (LBFavoriteChampionButton*)[[[cell contentView] viewWithTag:99] viewWithTag:52];
UIImageView *championImage = (UIImageView*)[[[cell contentView] viewWithTag:99] viewWithTag:51];
// Text
[championLabel setText:[item displayValue]];
[championImage setImageWithURL:[NSURL URLWithString:[item imageUrl]]];
// Fav button
[buttonFavorite setCurrentChampion:item];
if ([[self favoritesChampions] containsObject:item]) {
[buttonFavorite setSelected:YES];
[buttonFavorite setIsFavorite:YES];
} else {
[buttonFavorite setSelected:NO];
[buttonFavorite setIsFavorite:NO];
}
return cell;}
}
I've tried to log everything, I checked if "item" could be sometimes null and it's never the case. But when a cell disappears, the [[[cell contentView] subviews] count] is equal to 0, which normally should be 1.
I really don't understand what's happening here. I tested it on real devices + simulator and it happens with both of them.
2nt problem :
My other "problem" is that the scrolling of the tableview is not as smooth as i would expect it to be on the iPhone 4 (tested on iOS 6 and iOS 7, same result :-( ). I'm using SDWebImage to load images asynchronously and to cache them, I'm reusing cells, what could I do to improve the performances ? I tested on an iPhone 4S and had no problem, scrolling is very smooth.
Am I doing something wrong ? Any ideas about one of my problem ?
Thank you for taking the time to answer me.
EDIT : 1st attempt to solve the problem
Trying to customize cell with a custom UITableViewCell subclass :
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"LBListChampionCell";
LBListChampionCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil){
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"LBListChampionCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
LBSelectorChampionViewController *item = [self itemAtIndexPath:indexPath];
[[cell championName] setText:[item displayValue]];
[[cell championLogo] setImageWithURL:[NSURL URLWithString:[item imageUrl]]];
return cell;
}
I wasn't able for the moment to reproduce the "disappear" bug (sometimes it takes a moment to reproduce it...), but there is no improvement at all concerning the performances :(. Even while just setting a dummy text : [[cell championName] setText:#"Test"]; (and commenting the item part) the scrolling is still not really smooth.
Any ideas ?
EDIT 2 : Best solution (thanks to rdelmar)
Create a subclass of UITableViewCell and load the nib in viewDidLoad :
- (void)viewDidLoad
{
...
UINib *nib = [UINib nibWithNibName:#"LBListChampionCell" bundle:nil];
[[self tableView] registerNib:nib forCellReuseIdentifier:#"LBListChampionCell"];
}
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LBListChampionCell *cell = [tableView dequeueReusableCellWithIdentifier:#"LBListChampionCell"];
LBSelectorChampionViewController *item = [self itemAtIndexPath:indexPath];
[[cell championName] setText:[item displayValue]];
[[cell championLogo] setImageWithURL:[NSURL URLWithString:[item imageUrl]]];
...
}
It increased the scrolling performance (still not perfectly smooth on iPhone 4 but it works well). Moreover it seems that no cell are disappearing anymore :-)
Thanks rdelmar!
I think somehow LBSelectorChampionViewController *item = [self itemAtIndexPath:indexPath]; from one of your cell's contentView was added to another cell's contentView, which would cause the previous cell's contentView to have 0 subview.
I think you are getting the first problem because of cell reusability. LBSelectorChampionViewController *item = [self itemAtIndexPath:indexPath]; will not always be added to the [cell contentView] as cell may not be nil. In addition, there might be a situation when this view will be added to another cell and removed from the previous. Anyway, this will be easier to fix if you subclass UITableViewCell and make LBSelectorChampionViewController part of this custom cell. This will make everything easier and you will never meet reusability problems.
I am not exactly familiar with SDWebImage, but maybe the problem is that it doesn't cache images and tries to load new image every time [championImage setImageWithURL:[NSURL URLWithString:[item imageUrl]]]; is hit. I've used AFNetworking in a few projects to load images and have never had any problems with it.
Hope this is helpful!
Cheers!

How do I add a UIActivity Indicator to every Cell and maintain control of each individual indicator

I'm trying to add an activity indicator to certain cells in my UITableView. I do this successfully in the method didSelectRowAtIndexpath using
CGRect CellFrame = CGRectMake(260, 10, 20, 20);
actindicator = [[UIActivityIndicatorView alloc]initWithFrame:CellFrame];
[actindicator setHidesWhenStopped:NO];
[actindicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleGray];
actindicator.tag =1;
[[cell contentView] addSubview:actindicator];
The catch is I need to control these multiple activity indicators from ANOTHER METHOD. I figured a property was a way to do this however by initialising a new instance of actIndicator every time, I loose reference's to all but the 'latest' init of the activity indicator thus meaning I can only control one.
What do i need to do here (if even possible?) to maintain reference to all the actIndicators so i can begin animating ALL of them?
Or Can I somehow use the actindicator.tag to control some form of reference.
Many thanks for any help.
EDIT: (Derived from answer) to access all Instances of Activity indicator with a tag of 1 in tableView (visible cells only) can use below from another method:
for (UITableViewCell *cell in [self.tableView visibleCells]) {
UIActivityIndicatorView *actView = (UIActivityIndicatorView *)[cell.contentView
viewWithTag:1];
[actView startAnimating];
activityFlag = 1;
}
The method above will cycle through all visible cells and start animating the activity indicator.
To handle the case of the tableview being scrolled, I re-animate the indicators using the method below which is in cellForRowAtIndexPath. cellStateKey simply indicates if the cell has a checkmark next to it, if it does have a checkmark and my activityflag (async webserver call in progress)is set..then i want to continue animating.(technically re-start animation, as scrolling tableview stops it)
if ([[rowData objectForKey:cellStateKey] boolValue]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
if(cell.accessoryType == UITableViewCellAccessoryCheckmark &&activityFlag ==1){
for (UIView *sub in [[cell contentView] subviews]) {
if (sub.tag == 1) {
UIActivityIndicatorView *acView = (UIActivityIndicatorView *)
[cell.contentView viewWithTag:1];
[acView startAnimating];
}
}
As mentioned in origional question I initialise my activity indicators (and remove activity indicators aswell if required) in the method didSelectRowAtIndexPath
You can add UIActivityIndicatorView as cell's accessoryView.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.frame = CGRectMake(0, 0, 24, 24);
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryView = spinner;
[spinner startAnimating];
[spinner release];
}
A simple way to do this (assuming you're adding the indicator as in your code) is to first get a collection of the visible cells (or rows) in your table, by calling [tableView visibleCells]. Then iterate through the cells like this:
for (UITableViewCell *cell in [tableView visibleCells]) {
for (UIView *sub in [cell subViews]) {
if (sub.tag == 1) { // "1" is not a good choice for a tag here
UIActivityIndicatorView *act = (UIActivityIndicatorView *)sub;
[act startAnimating]; // or whatever the command to start animating is
break;
}
}
}
There's more complexity for you to deal with: in your original code, you need to make sure you're not adding an additional activity indicator to a pre-existing cell each time cellForRowAtIndexPath is called, and you need to account for the situation where the user might scroll the table at a later point, exposing cells that do not have their activity indicator turned on.
You need to make a custom UITableViewCell by extending it. Then have a UIActivityIndicatorView as a member of that Cell. Then you can access it and control it on a cell by cell basis.
Assuming that activity view indicator tag is unique in the cell.contentView you can try something like:
UITableViewCell *cell = [tableview cellForRowAtIndexPath:indexpath];
UIActivityIndicatorView *acView = (UIActivityIndicatorView *)[cell.contentView viewWithTag:1];
//acView will be your activitivyIndicator

UITableView discloure indicator going nuts

I have a UITableView (on a UIViewController) which is pushed via a navigationController. Along with pushing, I select with which array i want to populate the table. The code for pushing is like this:
if(self.newView == nil)
{
NewView *viewTwo = [[NewView alloc] initWithNibName:#"Bundle" bundle:[NSBundle mainBundle]];
self.newView = viewTwo;
[viewTwo release];
}
[self.navigationController pushViewController:self.newView animated:YES];
newView.tableArray=newView.arrayWithOptionOne;
[newView.tableView reloadData];
All works well and the table gets reloaded every time. However in the last row of section 0, there is a switch which loads section 1.
The last row of section 1 is tappable (didSelect…) and it loads a modalView. On this last rod I added a disclosure indicator and also the blue background when tapping. The table has sliders, labels, etc. So the customization is quite long:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = nil;
static NSString *kDisplayCell_ID = #"DisplayCellID";
cell = [tableView dequeueReusableCellWithIdentifier:kDisplayCell_ID];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:kDisplayCell_ID] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else
{
UIView *viewToRemove = nil;
viewToRemove = [cell.contentView viewWithTag:1];
if (viewToRemove)
[viewToRemove removeFromSuperview];
UIView *viewToRemove2 = nil;
viewToRemove2 = [cell.contentView viewWithTag:2];
if (viewToRemove2)
[viewToRemove2 removeFromSuperview];
}
if (indexPath.section==0) {
UIControl *cellValueS = [[[arrayTable objectAtIndex:indexPath.section] objectForKey:kViewKey] objectAtIndex:indexPath.row];
[cell.contentView addSubview:cellValueS];
}
if (indexPath.section==0 && indexPath.row==3) {
UIControl *cellValueL = [[[arrayTable objectAtIndex:indexPath.section] objectForKey:kViewLabel] objectAtIndex:0] ;
[cell.contentView addSubview:cellValueL];
}
if (indexPath.section==1 && indexPath.row==0){
UIControl *cellValueS = [[[arrayTable objectAtIndex:indexPath.section] objectForKey:kViewKey] objectAtIndex:indexPath.row] ;
[cell.contentView addSubview:cellValueS];
}
if (indexPath.section==1 && indexPath.row==1) {
UIControl *cellValueS = [[[arrayTable objectAtIndex:indexPath.section] objectForKey:kViewKey] objectAtIndex:indexPath.row] ;
[cell.contentView addSubview:cellValueS];
UIControl *cellValueL = [[[arrayTable objectAtIndex:indexPath.section] objectForKey:kViewLabel] objectAtIndex:0] ;
[cell.contentView addSubview:cellValueL];
}
if (indexPath.section==1 && indexPath.row==2) {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
}
return cell;
}
So far also works ok.
The problem is that when I go back to the previous view and select another option to populate the table, when it's pushed again, I see the disclosure indicator and blue selection background on other rows on the same section. I've observed that it depends on where the table is scrolled.
I've tried to understand why does it happen, but i can't. I've somehow solved the problem by setting newView to nil and releasing it and then allocating it again before it gets pushed again.
Am I doing something wrong here? or why is the disclosure indicator and tapping background appearing where they are not supposed to be?
Thanks in advance!
action of the switch
-(void)extraOptionsSwitchAction:(id)sender{
switch(extraOptionsSwitch.isOn) {
case 1:
// [self.tableView beginUpdates];
[self.tableView insertSections: [NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
//[self.tableView reloadData];
// [self.tableView endUpdates];
break;
case !1:
// [self.tableView beginUpdates];
[self.tableView deleteSections: [NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
// [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView reloadData];
// [self.tableView endUpdates];
break;
}
}
It has to to with reusing cells. You probably don't differentiate between the different cell types in your cell creation method - so one cell that went offscreen can easily be reused for another (different type) cell. Further, you seem to add subviews over and over again - only do that when you instantiate the cell (with alloc/int), and not when configuring.
Also, for different cell types, use different identifiers (you didn't show this code).
The programming guides have good example on table views and their cells and reuse pattern. It's worth reading a couple of times - it's easy to get wrong and is a main topic for performance tuning.
Edit
Now that you added more code, another problem seems to be here:
if (indexPath.section==1 && indexPath.row==2) {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
}
You are missing an else part. The selectionStyle and accessoryType are set to what they were set to before - you miss to configure the cells correctly for all other cells than that special one.
Each cell type should really get its own identifier though. If the adding/removing of subviews work as expected is hard to tell from that code.
One thought: As you aren't really reusing a lot of the cells here anyhow you could even disable the reuse by changing
static NSString *kDisplayCell_ID = #"DisplayCellID";
cell = [tableView dequeueReusableCellWithIdentifier:kDisplayCell_ID];
to
static NSString *kDisplayCell_ID = nil;
This would just always produce a new cell. I wouldn't recommend this in the general case, though.
This is due to cellReusability, and it has, for a long time, wasted so many developers' time and effort. If you knew the concept of cell reusability, you would know that while scrolling, the index of the row and sections remain the same (although you expect it to be different for a different position on the uiTableView).
The only way is to subClass the UITableViewCell and create your own CustomUITableViewCell, and implement that with the disclosure indicator, or resist your input to just a small TableView that fits the screen and make scrollable = NO.

How to pass a NSString from a uitableviewcell to an NSObject

I am trying to pass the text lable that is in the uitableviewcell I have created but I am not sure of the parameters that are needed in order to do this.
I have an NSObject with a method that has an NSString object as a parameter, this is where I would like to pass the information from my uitableviewcell label too...
I have #imported the .h file and set up the object inside the views tableViews:didSelectRowAtIndexPath method but I am just not sure how to pass it the text from the lable, this is what I have tried but I have a compatibility warning..
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//initalize object to hold search parameters
VehicleSearchObject *vehicleSearchObject = [[VehicleSearchObject alloc] init];
[self.navigationController popViewControllerAnimated:YES]; //pops current view from the navigatoin stack
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//--- this if block allows only one cell selection at a time and is passing the text in the cells label back to the object
if (oldCheckedData == nil) { // No selection made yet
oldCheckedData = indexPath;
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
[vehicleSearchObject GetManufacturers:[[cell textLabel] objectAtIndex:[indexPath row]] // This is where I try to pass the text back but not sure how to do it..
}
else {
UITableViewCell *formerSelectedcell = [tableView cellForRowAtIndexPath:oldCheckedData]; // finding the already selected cell
[formerSelectedcell setAccessoryType:UITableViewCellAccessoryNone];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark]; // 'select' the new cell
oldCheckedData=indexPath;
}
}
You have to do this.
[vehicleSearchObject GetManufacturers:cell.textLabel.text];
You can reference the cell label text by simply doing:
cell.textLabel.text
i.e.
[vehicleSearchObject GetManufacturers:cell.textLabel.text];
[[cell textLabel] setText:[vehicleSearchObject GetManufacturers:[[cell textLabel] text]];

UITableView scrollToRowAtIndexPath only scrolls sometimes

I have a UITableView with several UITableViewCells and inside these cells are UITextFields. I implemented a UIToolbar for switching between the different textfields. I can go to the next textfield in the next cell or to the previous textfield. This works fine until a cell should become first responder which is currently not visible in the tableview.
I figured out that
[self.tableview cellForRowAtIndexPath:indexPath]
returns nil and that manually scrolling to the cell with the textfield, which should become first responder, removes the problem. Therefore I tried scrolling to the cell before changing it to become first responder.
I tried that with
[[self.tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
but unfortunately the tableview does not scroll to this cell but to all other cells. When I add a breakpoint to this line the problem doesn't occur. It also looks like the tableview is scrolling to the cell but then scrolls down again.
Here is the code of cellForRowAtIndexPath:
UITableViewCell *cell;
static NSString *AttributeCellIdentifier = #"AttributeCellIdentifier";
cell = [tableView dequeueReusableCellWithIdentifier:AttributeCellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"AttributeCell" owner:self options:nil];
cell = attributeCell;
self.attributeCell = nil;
UITextField * textField = (UITextField *)[cell viewWithTag:1];
[textField setKeyboardType:UIKeyboardTypeDecimalPad];
[textField setInputAccessoryView:[self inputAccessoryView]];
}
UITextField * textField;
textField = (UITextField *)[cell viewWithTag:1];
textField.placeholder = #"C(Probe)/mol/l";
return cell;
Here is the code for switching to the previous textfield:
UIView * currentResponder = [self.view findFirstResonder];
UITextField * newFocusTextField;
UITableViewCell * cell = (UITableViewCell*)[[currentResponder superview] superview];
NSIndexPath* indexPath = [self.tableView indexPathForCell:cell];
newFocusTextField = (UITextField*)[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]] viewWithTag:1];
[currentResponder resignFirstResponder];
[newFocusTextField becomeFirstResponder];
UITableViewCell * newCell = (UITableViewCell*)[[newFocusTextField superview] superview];
NSIndexPath * newIndexPath = [self.tableView indexPathForCell:newCell];
[self.tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Hope this helps.
The code on your post and the problem definition is not hundred percent clear, but there was a bug/glitch in scroll methods' working for iOS 3.0 (i guess specifically 3.0, as far as i remember). I couldnot figure out the reason for it to happen, but it was crashing all the time, although it was OK for iOS 4.x.
The fix was adding a
[table reloadData]
before the scroll line. I know it doesnt make sense, and I know it is not good practice especially if the cell rendering is expensive, but it did solve the problem for that case. I dont know if your problem stems from the same issue, but you may just give it a try...
I tried several thinks and everything works now fine. I removed:
[currentResponder resignFirstResponder];
And after removing that line the tableview scrolled fine and I can directly set the pointer to the new cell with the newFocusTextField and make it firstResponder with:
[newFocusTextField becomeFirstResponder];
I have a nextTextField method which works fine with
[currentResponder resignFirstResponder];
so I don't really understand why the problem occurs but I think scrolling to the cell and then just setting the new firstResponder is better then resigning the currentResponder first.
Thank you for helping anyway ;)