after updating to iOS8 and compiling my app with XCode6, I get a very strange exception when clicking on a button.
My button is defined like this:
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self
action:#selector(cellButtonPressed:)
forControlEvents:UIControlEventTouchDown];
In "#selector" I defined the method which is called when button is pressed:
-(void) cellButtonPressed:(id)sender {
NSLog(#"Hello again");
}
I also added this method to my header file .h
This button is placed into a UITableViewCell as a subview:
button.frame = CGRectMake(cell.frame.size.width - 54, cell.frame.origin.y+20, 36, 36);
[cell addSubview:button];
This worked very fine on iOS7. But now, on iOS8, I get an exception after clicking on the button:
2014-09-25 08:33:47.461 ****[12442:1669165] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewWrapperView type]: unrecognized selector sent to instance 0x12ce35560'
*** First throw call stack:
(0x185dae084 0x1963940e4 0x185db5094 0x185db1e48 0x185cb708c 0x10005a928 0x18a5652f4 0x18a54e44c 0x18a56dff8 0x18a524724 0x18a55e7b8 0x18a55de58 0x18a531660 0x18a7cfd6c 0x18a52fbc8 0x185d66324 0x185d655c8 0x185d63678 0x185c91664 0x18edd35a4 0x18a596984 0x10004b324 0x196a02a08)
libc++abi.dylib: terminating with uncaught exception of type NSException
Does anybody know why?
Thx for you help!
Ok, I found the answer because of an advice in comments:
The method in selector is called properly. Problem exists in getting cell from superview.
On iOS7 I get the clicked button within cell with following code:
UserTableViewCell *cell = (UserTableViewCell *)[[sender superview] superview];
Now, on iOS8, I have to get the cell with this call:
UserTableViewCell *cell = (UserTableViewCell *)[sender superview];
So, this is a solution for me:
-(void)cellButtonPressed:(id)sender {
NSArray *vComp = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:#"."];
UserTableViewCell *cell = nil;
if ([[vComp objectAtIndex:0] intValue] >= 8) {
cell = (UserTableViewCell *)[sender superview];
} else {
cell = (UserTableViewCell *)[[sender superview] superview];
}
// do your stuff
}
So, view stack of TableViewCell seems to be another. Good to know :)
Thanks guys!
Related
The following problem occurs in my project, when i pressed and hold a backbutton (uibarbuttonitem) and a tableview row. And letting go the tableview row and followed by letting go the backbutton the following error occurs:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'.
I have figured out the problem here is that the tableview didselectrowatindexpath has wrong indexpath.
Case: A uibarbuttonitem and a tableview row is pressed and hold simultaneously.
In situation 1: The uibarbuttonitem is letting go and followed by letting go the tableview row.
Result: Selector for uibarbuttonitem is called. (In my case no error.)
In situation 2: The tableview row is letting go and followed by letting go the uibarbuttonitem.
Result: Selector for uibarbuttonitem is called and followed by the delegate methods of tableview. (In my case the error above occurs.)
- (void)backbutton {
self.itemList = [self getOldItem];
[self.tableView reloadData];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// It crash on this line.
Model *model = [[self.itemList objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
[model show];
}
Does anyone know if this is a normal behavior for which order the selector methods of those button are called? And how i should handle this?
I'm trying to delete multiple selections from a table view. Everything works fine until I scroll up or down, then it crashes and throws an exception.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
Object can not be nil
This is how I am deleting the objects :
- (IBAction)deleteObjects:(id)sender {
NSArray *selectedRows = [self.tableView indexPathsForSelectedRows];
BOOL deleteSpecificRows = selectedRows.count > 0;
if (deleteSpecificRows)
{
NSMutableArray *stringsOfObjects = [NSMutableArray new];
for (NSIndexPath *selectionIndex in selectedRows) {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectionIndex];
[stringsOfObjects addObject:cell.textLabel.text];
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *plistPath = [path stringByAppendingPathComponent:#"AlertSubscriptions.plist"];
NSMutableArray *array = [[[NSMutableArray alloc] initWithContentsOfFile:plistPath] mutableCopy];
[array removeObjectsInArray:stringsOfObjects];
[self.alertSubArray removeObjectsInArray:stringsOfObjects];
[array writeToFile:plistPath atomically:YES];
[self.tableView deleteRowsAtIndexPaths:selectedRows withRowAnimation:UITableViewRowAnimationAutomatic];
}
Again this all works fine, unless I scroll up/down to select/deselect more cells so I subclassed my cells because I read that won't reuse cells on SO.
For reference:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SubscriptionsTableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:nil];
if (cell == nil) {
cell = [[SubscriptionsTableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:nil];
}
cell.textLabel.text = [self.alertSubArray objectAtIndex:indexPath.row];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
return cell;
}
Ive tried it with a static cell and without. I've tried setting dequeueReusableCellWithIdentifier to a static cell and without. Neither work when I scroll
static NSString *CellIdentifier = #"Cell";
Error log:
2015-06-28 15:46:19.379 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
*** First throw call stack:
(0x186d3c2d8 0x1985680e4 0x186c234c0 0x10017e7d8 0x18b7b1404 0x18b79a4e0 0x18b7b0da0 0x18b7b0a2c 0x18b7a9f68 0x18b77d18c 0x18ba1e324 0x18b77b6a0 0x186cf4240 0x186cf34e4 0x186cf1594 0x186c1d2d4 0x1904336fc 0x18b7e2fac 0x1001646d4 0x198be6a08)
libc++abi.dylib: terminating with uncaught exception of type NSException
EDIT
So after trying what the others have told me to do I have done the following:
Set an exception breakpoint. The line that populates after crash is the [stringsOfObjects addObject:cell.textLabel.text]; line.
i have made sure my cellForRowAtIndexPath method was set up properly now:
}
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.backgroundColor = [UIColor whiteColor];
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
[self.tableView setRowHeight:45];
cell.textLabel.text = [self.alertSubArray objectAtIndex:indexPath.row];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
return cell;
}
After that I still get a crash at adding the objects to the NSMutableArray so I looked in the debugger and made sure my UITableViewCell isn't nil like Robot mentioned to me, and it looks like it is but I don't know where to go from here: because in my mind it is not nil
As you can see, I have selected 6 rows but it only added 2 objects. I don't know why this is so difficult, why is it nil when some aren't? And why can I delete them perfectly fine without scrolling to select more?
So, after an extensive discussion in the comments the problem seems to be the following:
The logic of [stringsOfObjects addObject:cell.textLabel.text]; in the deleteObjects: method is wrong. This is because it is taking the text direct from the cells rather than the array backing store that populates the cells.
Cells can be scrolled offscreen and re-used so the text in them is no longer correct and, in fact, the cell no longer "exists" as it has been reused. If the cell doesn't "exist" an empty cell will be created where the text field might be nil. Note that cell re-use is a good thing; don't create cells and never re-use them or you will run out of memory fast.
Instead, take the text from your backing store that populates the cells themselves rather than from the cell directly. I would expect code something like:
[stringsOfObjects addObject:[self.alertSubArray objectAtIndex:selectionIndex.row]];
I have a custom UITableViewCell with a class linked to it called customCell.m. (I didn't use xib.) In the cell there is a button. Is there a way to create the buttons action on the mainVC.m file, as apposed to customCell.m?
Update
Here is the code I tried implementing. What I did is, I called a method from mainVC.m.
CustomCell.m
- (IBAction)myButton:(id)sender
{
CategorieViewController *mainVC = [[CategorieViewController alloc] init];
[mainVC myMethod];
}
MainVC.m
- (void)myMethod:(id)sender
{
UITableViewCell *clickedCell = (UITableViewCell *)[[[sender superview] superview] superview];
NSIndexPath *clickedButtonPath = [self.myTableView indexPathForCell:clickedCell];
NSLog(#"%#", clickedButtonPath);
}
CategorieViewController myMethod]: unrecognized selector sent to instance 0x7fd2dbd52a00
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CategorieViewController myMethod]: unrecognized selector sent to instance 0x7fd2dbd52a00'
You're calling myMethod, but the method is actually myMethod: and takes the sender as a parameter. Try changing:
[mainVC myMethod];
to:
[mainVC myMethod:sender];
Also, any sender you currently pass to myMethod: as a parameter, won't belong to mainVC's tableview yet because you're creating a brand new CategorieViewController instance to perform the method call and its table has never been loaded.
Assuming MainVC is the visible view controller, you can change:
CategorieViewController *mainVC = [[CategorieViewController alloc] init];
to:
UINavigationController *nav = (UINavigationController*)self.window.rootViewController;
CategorieViewController *mainVC = (CategorieViewController*)nav.visibleViewController;
to get the current the current MainVC instance with the loaded tableview.
I'm getting the following error when I addTarget to UIButton inside UiCollectionView.
My code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIButton *btnBonus = (UIButton *) [cell viewWithTag:405];
[btnBonus setTag: [arrayTruebonusTags[0] intValue]];
[btnBonus addTarget:self action:#selector(goBonus:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
- (void) goBonus:(id) sender
{
UIButton *button = (UIButton *) sender;
}
And I get this error:
[Controller goBonus]: unrecognized selector sent to instance 0x16dc1190
2014-11-08 11:11:41.991 demo[3570:1707966] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Controller goBonus]: unrecognized selector sent to instance 0x16dc1190'
*** First throw call stack:
(0x299cdc1f 0x375b2c8b 0x299d3039 0x299d0f57 0x29902df8 0x2cebdc2b 0x2cebdbd1 0x2cea8863 0x2cebd63d 0x2ce8242d 0x2ceb72f1 0x2ceb6bcd 0x2ce8d3dd 0x2d100c29 0x2ce8be39 0x29994377 0x29993787 0x29991ded 0x298e0211 0x298e0023 0x30cbf0a9 0x2ceec1d1 0xf3599 0x37b32aaf)
libc++abi.dylib: terminating with uncaught exception of type NSException
The problem is that if I do the same without goBonus: and in the method -goBonus{} it works like a charm.
The crash log you posted complains about a missing method [Controller goBonus].
The code you posted shows you adding the action goBonus: (with a colon, meaning it takes a parameter).
The fact that the crash doesn't match your code tells me that you have a mismatch somewhere. The selector in your addTarget method, #selector(goBonus:), is correct for the method you posted, but the crash log is complaining about a missing selector #selector(goBonus) (no colon, hence no parameter.)
You need to sort that out.
I'm trying to print on/off when the user changes the state of my UISwitch.
For example
- (IBAction)toggleSwitch:(id)sender {
ChannelsTableViewCell* cell = (ChannelsTableViewCell *)[sender superview].superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
if (cell.localSwitch.on)
{
NSLog(#"On");
}
}
When I run this it throws an error.
2014-04-24 11:33:41.462 HRApp[3258:60b] -[UITableViewCellScrollView localTitle]: unrecognized selector sent to instance 0x19354410
2014-04-24 11:33:41.467 HRApp[3258:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCellScrollView localTitle]: unrecognized selector sent to instance 0x19354410'
If you hook up the IBAction to the switch, you don't need to get the UITableViewCell to check for the switch's value. You can use the sender parameter from the IBAction. Hence:
- (IBAction)toggleSwitch:(id)sender {
UISwitch *switch = (UISwitch *)sender;
if (switch.on)
{
NSLog(#"On");
}
}
If you need to find the indexPath at which the UISwitch is shown, you can add the following:
CGPoint pointInTable = [switch convertPoint:switch.bounds.origin toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:pointInTable];