Regarding Crash app in iphone - iphone

below code is working in ios 6 , but in ios 7 it is cause of crash app at the point of "indexPathForCell". help me to resolve this problem.. there is table view in which we are clicking in a cell and moving in a second view again table view is going to be load with data...but before that app crash..
Thanks in advance
UIView *view = [self superview];
// Find TableViewCell
if(view != nil && ![view isKindOfClass:[UITableView class]]) view = [view superview];
UIView *cellView = [self superview];
// Find TableViewCell
if(cellView != nil && ![cellView isKindOfClass:[UITableViewCell class]]) cellView = [cellView superview];
if(view != nil && cellView != nil) {
UITableViewCell *cell = (UITableViewCell*)cellView.superview.superview;
UITableView *tableView = (UITableView*)view;
if([tableView style] == UITableViewStyleGrouped)
{
NSIndexPath *path = [tableView indexPathForCell:(UITableViewCell*)cell];
if(path) {
int count = [tableView numberOfRowsInSection:[path section]];

UITableViewCell cell = (UITableViewCell)cellView.superview.superview.superview;
For ios7 you need to add one more superview.
Your app is getting crashed due to this
[tableView indexPathForCell:(UITableViewCell*)cell];
because cell is nil.
You need to add a check for ios 7 and ios 6.
For ios 7 Add another superview it will solve the purpose.

Related

Textfield in tableview cell crashes on iOS7

My code is working fine on iOS6 but crashing in iOS7.
I comparing the text field with table view cell text-field.
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
DLog(#"-> %#", textField.text);
PhotoViewCustomCell *cell = (PhotoViewCustomCell*)[[textField superview] superview];
NSIndexPath *indexPath = [tblPhotoDetail indexPathForCell:cell];
//PhotoViewCustomCell *cell = (PhotoViewCustomCell *)[tblPhotoDetail cellForRowAtIndexPath:indexPath];
PhotoInformation *objPhotoInfo = [selectedPhotos objectAtIndex:indexPath.row];
if ([textField isEqual:cell.mytextfield])
{ =========>crashing in this line
do something
}
else
{
do something
}
}
Error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCellScrollView mytextfield]: unrecognized selector sent to instance 0xdc119a0'
Add additional superview call to get your cell. Looks like you are getting hidden UITableViewCellScrollView which goes in hierarchy immediately above contentView
PhotoViewCustomCell *cell = (PhotoViewCustomCell*)[[[textField superview] superview] superview];
The code in Accepted Answer is not the safer one and also can't be used for iOS 6. I'd rather recommend you to use this code. :
UIView *view = [textField superview];
while (![view isKindOfClass:[PhotoViewCustomCell class]]) {
view = [view superview];
}
PhotoViewCustomCell *cell = (PhotoViewCustomCell *)view;

How do I retrieve UITableView row number of a UISwitch?

I have tried several approaches posted here, but I cannot get my table full of switches to return an index value for the cell of the changed switch. I am creating the view containing the table programmatically (no xib).
TableSandboxAppDelegate.m I instantiate the view controller in didFinishLaunchingWithOptions: with:
...
TableSandboxViewController *sandboxViewController = [[TableSandboxViewController alloc]
init];
[[self window] setRootViewController:sandboxViewController];
...
TableViewController.h file reads:
#interface TableSandboxViewController : UITableViewController
{
NSMutableArray *_questionOrder;
NSMutableArray *switchStates;
}
#end
TableViewController.m cellForRowAtIndexPath: reads:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MainCell"];
UISwitch *theSwitch = nil;
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"MainCell"];
theSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
theSwitch.tag = 100;
[theSwitch addTarget:self action:#selector(switchChanged:)
forControlEvents:UIControlEventValueChanged];
[cell.contentView addSubview:theSwitch];
} else {
theSwitch = [cell.contentView viewWithTag:100];
}
if ([[switchStates objectAtIndex:indexPath.row] isEqualToString:#"ON"]) {
theSwitch.on = YES;
} else {
theSwitch.on = NO;
}
return cell;
TableViewController.m -(IBAction)switchChanged:(UISwitch *)sender reads:
UITableViewCell *theParentCell = [[sender superview] superview];
NSIndexPath *indexPathOfSwitch = [self.tableView indexPathForCell:theParentCell];
NSLog(#"Switch changed at index: %d", indexPathOfSwitch.row);
My log result is always "Switch changed at index: 0". I feel like the problem is in that CGPoint line where I've tried combinations of replacements for "sender" ([sender superview], [[sender superview]superview], etc). I don't feel like that line is pointing to the view that displays the table.
What am I doing wrong?
Note added 10/9, 9:15 EDT: my goal is to be able to handle about 100 yes/no questions in the table, so reuse is a key. I want to scroll and have the table the state of each switch, as well as be able to retrieve them when leaving the view.
Tags is an okay solution, but a little clumsy because the cells - and therefore their subviews - are continually being reused, changing their rows - and therefore the tags they need.
Instead, I generally keep one of these around:
- (NSIndexPath *)indexPathWithSubview:(UIView *)subview {
while (![subview isKindOfClass:[UITableViewCell self]] && subview) {
subview = subview.superview;
}
return [self.tableView indexPathForCell:(UITableViewCell *)subview];
}
Then when I get an IBAction:
- (IBAction)someSubviewAction:(id)sender {
NSIndexPath *indexPath = [self indexPathWithSubview:(UIView *)sender];
// carry on from here
}
You may set switch view tag to row index. Instead of theSwitch.tag = 100;
do
-(UITableViewCell*)tableView:table cellForRowAtIndexPath:indexPth
{
UISwitch *theSwitch = nil;
if (cell == nil) {
...
// as per your example
[cell.contentView addSubview:theSwitch];
} else {
theSwitch = subviewWithClass(cell.contentView, [UISwitch class]);
}
theSwitch.tag = indexPath.row;
...
}
Add this helper function to replace viewWithTag: call
UIView *subviewWithClass(UIView *contentview, Class klass)
{
for (UIView *view in contentview.subviews)
if ([view isKindOfClass:klass])
return view;
return nil;
}
Then retrieve tag, that is a row index now, in your switchChanged function
-(IBAction)switchChanged:(UISwitch *)sender {
NSLog(#"Selected Switch - %d", sender.tag);
...
}
If you use something block-based (like https://github.com/brightsoftdev/iOS-Block-Based-Bindings/blob/master/UISwitch%2BBindings.m), you don't need to worry about getting the row, because you can reference the indexPath that is passed into tableView:cellForRowAtIndexPath: in your block.
Similar to #danh, I've come up with this solution using an extention which I've used multiple times.
#interface UIView (Find)
- (id)findSuperviewOfClass:(Class)class;
- (NSIndexPath *)findIndexPath;
#end
#implementation UIView (Find)
- (id)findSuperviewOfClass:(Class)class
{
return [self isKindOfClass:class] ? self : [self.superview findSuperviewOfClass:class];
}
- (NSIndexPath *)findIndexPath
{
UITableView *tableView = [self findSuperviewOfClass:[UITableView class]];
return [tableView indexPathForCell:[self findSuperviewOfClass:[UITableViewCell class]]];
}
#end
for iOS6+ you could maintain a NSMutableArray queuedSwitches
in -tableView:cellForrowAtIndexPath: you would take a switch, if not empty and places it on the custom cell and assign it to a property. If empty you create a new one.
in -tableView:didEndDisplayingCell:forRowAtIndexPath: you would add it to quededSwitches and remove it from it cell.
This will just allocate enough switches for visible cells and reuse them.
the switches are all wired up to one action.
-(void)switchAction:(UISwitch *)switch
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:[switch superView]];
//…
}
You could create a subclass of UISwitch and add an indexPath property, then just set the indexPath in cellForRowAtIndexPath:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
SwitchCell *returnCell = [tableView dequeueReusableCellWithIdentifier:#"SwitchCell" forIndexPath:indexPath];
returnCell.switch.indexPath = indexPath;
return returnCell;
}

Add subview to UITableViewCell: Added several times when scrolling

I have some different cells in my tableView, each with different subviews. Each time a cell dissapear and reapear, the subview is addad on top of the old view, and also is added to other cells. What is the correct way to add subviews to cells without using custom cells?
Thanks in advance
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"StatisticsCell";
StatisticsCell *cell = (StatisticsCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray *topLevelObject = [[NSBundle mainBundle] loadNibNamed:#"StatisticsCell" owner:nil options:nil];
for (id currentObject in topLevelObject)
{
if ([currentObject isKindOfClass:[UITableViewCell class]])
{
cell = (StatisticsCell *)currentObject;
break;
}
}
}
if (indexPath.section == 0 && indexPath.row == 0)
{
//Add a subview here
[cell addsubview ....
}
else if (indexPath.section == 0 && indexPath.row == 1)
{
//Add a subview here
[cell addsubview ....
}
etc....
Whenever you scroll cell for row method is called, so when your cell be visible, it will add subview to the cell. Place a check that is view added already, make an ivar that is bool, and set it true when you add view, and false when you remove. Like this:
.
.
.
if (indexPath.section == 0 && indexPath.row == 0 && isFirstViewAlreadyAdded== NO)
{
//Add a subview here
[cell addsubview ....
isFirstViewAlreadyAdded = YES;
}
else if (indexPath.section == 0 && indexPath.row == 1 && isSecondViewAlreadyAdded == NO)
{
//Add a subview here
[cell addsubview ....
isSecondViewAlreadyAdded = YES;
}
.
.
.
You can check whether that subView is already added to cell or not.
UIView *subView = [tableCell viewWithTag:tagOfYourSubView];
if (subView) {
//subView exists
}
else {
//subView does not exist
}
If it is not added then you can add.
Don't add the subview every time..
You should add the subview in the if(cell==nil) block.
And after that you can set the hidden property as true or false according to indexpath.row.
like:
if (indexpath.row == 0)
img1.hidden = FALSE;
else
img1.hidden = TRUE;

How to imitate rotation from the photos app on thumbnail view

I have an app that mimics the photos app in that it displays a table of thumbnails which represent an album that the user has clicked on. I'm having trouble imitating the animation of this window when rotation is occurred. For your reference the window I'm talking about is below:
I'm able to get the end result to turn out fine (ie after the animation is complete, everything works), however my rotation doesn't look as good as the photos app. The photos app actually looks like it's rotating the window and you can barely notice the photos resetting themselves. On mine, you can see the thumbnails sort of moving around and it doesn't look as good.
Does anyone have a clue as to what the photos app is doing? Is it possible that it's speeding up the animation, or maybe blurring it in the middle so you can't see what's going on? My code is listed below:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
else
{
for (UIButton *photoButton in [cell subviews])
{
[photoButton removeFromSuperview];
}
}
NSInteger photosPerRow;
if (self.interfaceOrientation == UIInterfaceOrientationPortrait)
{
photosPerRow = 4;
}
else
{
photosPerRow = 6;
}
CGRect frame = CGRectMake(4, 2, 75, 75);
for (int i = [indexPath row] * photosPerRow; i < (([indexPath row] * photosPerRow) + photosPerRow) && i < [[self photos] count]; i++)
{
[[photos objectAtIndex:i] setFrame:frame];
[cell addSubview: [photos objectAtIndex:i]];
frame.origin.x = frame.origin.x + frame.size.width + 4;
}
return cell;
}
Then here is how I'm animating it in my tableview controller:
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
[self.tableView reloadData];
return (toInterfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
UPDATE: Well, I've tried a lot. So far switched all my code to a scroll view, then tried to have the thumbnails animated as follows when a rotation occurs: fade out the thumbnail, move it to it's new location, then fade it in. Of course I only did that with UIThumbViews that needed to be moved. I coming to the conclusion that apple must be using two views in order to pull them off. One view with the old set of thumbnail views, then rotating over the new set of thumbnail views. Any ideas?
UPDATED: This takes two lines of code, and you have to implement the thumbnails view in a tableview, not a scrollView:
[self.tableView reloadRowsAtIndexPaths: [self.tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];

Updating UITableViewCell dynamically

How can one change the textLabel or the detailTextLabel of a specific UITableViewCell dynamically?
Basically, I am downloading a set of files from the internet whenever the user taps on a cell. Now instead of the progress indicator, I would like to display the name of the file currently being downloaded. I would like to keep updating the detailTextLabel to show the (current) filenames until all the files are downloaded.
Assuming you're in a method like tableView:didSelectRowAtIndexPath: (or have another way to get the index path to the cell), you can do something like this:
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
UILabel *label = nil;
for(UIView *subview in cell.contentView.subviews) {
if([subview isKindOf:[UILabel class]]) {
label = (UILabel*)subview;
break;
}
}
if(label)
label.text = #"New text";
This will find the first UILabel in the cell, and may not work if your cell has more than one label. A more robust way to do it is to set the tag of the label when you create the cell (this can be done in Interface Builder or in code). Then you can find the label like this:
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
UILabel *label = nil;
for(UIView *subview in cell.contentView.subviews) {
if([subview isKindOf:[UILabel class]] && [subview tag] == kDetailLabelTag) {
label = (UILabel*)subview;
break;
}
}
if(label)
label.text = #"New text";
Just in case anyone still has this problem I have had success with calls to the reloadData method. For example in my code in the viewWillAppear method, I have the line:
[tableSettings reloadData];
Where tableSettings is the name of my UITableView.