I want to enable an UIAlertView-Request before the user can delete an item from my tableview. But the indexPath seems to be "nil"...
That is my coding:
The header-file looks like that ..
#interface ReturnRootViewController : UITableViewController <NSFetchedResultsControllerDelegate> {
// ..
NSIndexPath *deleteSelectedRow;
}
// ..
#property (nonatomic, retain) NSIndexPath *deleteSelectedRow;
#end
.. the implemtation file looks like that ..
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSUserDefaults *setting = [NSUserDefaults standardUserDefaults];
[setting synchronize];
// for the UIAlert-View handling:
deleteSelectedRow = indexPath;
if ([setting boolForKey:#"delete_preference"]) {
// Sicherheitsabfrage vor dem Löschen
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Achtung", #" ")
message:NSLocalizedString(#"Sind Sie sicher, dass Sie den Eintrag löschen wollen?", #" ")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Nein", #"Nein")
otherButtonTitles:NSLocalizedString(#"Ja", #"Ja"), nil];
[alert show];
[alert release];
} else {
// just the case you override the settings
[moc deleteObject:[fetchedResultsController objectAtIndexPath:deleteSelectedRow]];
NSError *error = nil;
if (![moc save:&error]) {
NSLog(#"Ungelöstes Problem %#, %#", error, [error userInfo]);
abort();
}
}
}
}
and last but not least the method for the uialertview-handling:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 1) {
// Delete request
[moc deleteObject:[fetchedResultsController objectAtIndexPath:deleteSelectedRow]];
// ..
and that is the place where my app dumps! deleteSelectedRow is nil.
by the way: deleteSelectedRow is not nil in the else-Statement from the tableview:commitEditingStyle-method! I'm fully helpless.
Before iOS 5 it works like that ...
Thanks for any hints.
ifeelhorst
As it is now, indexPath could be released by the time the alert view is clicked/returns. Change
deleteSelectedRow = indexPath;
to
self.deleteSelectedRow = indexPath; // to retain it
or (to make a copy of it)
self.deleteSelectedRow = [[indexPath copy] autorelease];
This does not explain why it is nil however, try putting a breakpoint on that line and on the alert view clickedButtonAtIndex callback to see what happens.
I think it's because you're assigning pointers rather than copying the object.
Related
I am trying to implement IAPs in one app but I'm still having difficulties with it. I followed various tutorials but all of them are out of date and full of errors. The only one that could work that I found is this one:
But I'm having a problem, the 3 products appear on my tableview but then when I click on one of them nothing happens... cell become blue and that's all... Am I missing something?
Or is that tutorial incomplete?
How do I run the purchase attempt?
Here is my code:
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
[productDetailsList addObjectsFromArray: response.products];
[productDisplayTableView reloadData];
}
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"Failed to connect with error: %#", [error localizedDescription]);
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [self.productDetailsList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *GenericTableIdentifier = #"GenericTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: GenericTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: GenericTableIdentifier];
}
NSUInteger row = [indexPath row];
SKProduct *thisProduct = [productDetailsList objectAtIndex:row];
[cell.textLabel setText:[NSString stringWithFormat:#"%# - %#", thisProduct.localizedTitle, thisProduct.price]];
return cell;
}
- (void)viewDidLoad
{
productDetailsList = [[NSMutableArray alloc] init];
productIdentifierList = [[NSMutableArray alloc] init];
for (short item_count=1; item_count <= 5; item_count++) {
[productIdentifierList addObject:[NSString stringWithFormat:#"com.mycompany.myapp.%d", item_count]];
}
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithArray:productIdentifierList]];
request.delegate = self;
[request start];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
You need to have something in the lines of:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([SKPaymentQueue canMakePayments])
{
SKProduct *selectedProduct = [self.productDetailsList objectAtIndex:indexPath.row];
SKPayment *payment = [SKPayment paymentWithProduct:selectedProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
}
Apple provides a decent step by step guide for handling in app purchases.
The main way to run IAPs involves a few different methods, but there are a few different steps you need to follow when implementing IAPs.
The first of these requirements are protocols. Please include each of the following protocols in your header file.
SKProductsRequestDelegate
SKPaymentTransactionObserver
SKRequestDelegate
You need the request methods:
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
if(response.products.count > 0)
{
SKProduct* product;
for(int i = 0; i<response.products.count; i++)
{
product = [response.products objectAtIndex:i];
if([product.productIdentifier isEqualToString:#"product identifier"])
{
self.currentProduct = product;
[self beginPaymentWithProduct:product];
}
}
}
}
I used the if statement to keep track of which product was being purchased. You will need an if-statement in that for-loop for each product identifier if you have multiple products. Use this later to unlock whatever on completion of the purchase.
You will also need the beginPayment method:
- (void)beginPaymentWithProduct:(SKProduct*)product
{
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
You also need the payment processing methods. I will not post all of them here as this would take far too much space, but I will give you the prototypes.
-(void)requestDidFinish:(SKRequest *)request;
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error;
- (void)recordTransaction:(SKPaymentTransaction *)transaction;
- (void)finishTransaction:(SKPaymentTransaction *)transaction wasSuccessful:(BOOL)wasSuccessful;
- (void)completeTransaction:(SKPaymentTransaction *)transaction;
- (void)restoreTransaction:(SKPaymentTransaction *)transaction;
- (void)failedTransaction:(SKPaymentTransaction *)transaction;
For each of your buttons in your table that are supposed to be purchasing, they will need to perform a method similar to this one on the didSelectRowAtIndex... method:
- (void)buyCoins:(id)sender
{
if([self canMakePurchases])
{
ualRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithArray:[NSArray arrayWithObjects: #"product identifier", nil]]];
[ualRequest setDelegate:self];
[ualRequest start];
}
}
This method will run the product request successfully. If you have all of these components in, you should have no problems.
I have used this code successfully in several apps.
I'm checking if file already exists and if it is, I'm alerting the user if he wants o replace the file. I'm using alert view and a delegate.
However when i run it using the simulator by the time the user selects YES or NO the program already run pass it and the blnVal has NO value regardless
I'm not sure what i'm missing here.?
(I searched the database here but couldn't find any related specific question)
-(void) chkFile2Save
{
short tst;
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* foofile = [documentsPath stringByAppendingPathComponent:pln2Save.text];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:foofile];
if(fileExists)
{
blnVal=NO;
[self AskFileSave];
}
//blnVal always NO for whatever reason ... ?
if(blnVal==NO)
tst=5;
//...
else {
tst=10;
//..
}
}
- (void) AskFileSave
{
UIAlertView *alertFileSave = [[UIAlertView alloc] initWithTitle:#"" message:#"File already exists. Override the file with current data?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alertFileSave setTag:10];
[alertFileSave show];
[alertFileSave release];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
//override file exists
if([alertView tag]==10)
{
if(buttonIndex == 1)
{
blnVal=YES;
}
else
{
blnVal=NO;
}
}
}
you are calling
[self AskFileSave];
that will execute
- (void) AskFileSave
than the execution will go back to :
//blnVal always NO for whatever reason ... ?
if(blnVal==NO)
tst=5;
//...
else {
tst=10;
//..
}
You should move that part of the code to delegate method, There you have the option what the user selected, because the UIAlerView will not stop the code execution.
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
//override file exists
if([alertView tag]==10)
{
if(buttonIndex == 1)
{
blnVal=YES;
tst = 10;
}
else
{
blnVal=NO;
tst=5;
}
}
}
I hope it helps!
I am using a UITableView to show data, and by using customise button and delete function I am trying to delete selected row. But i want to put an alertview inside that function when UITableView is empty, and by using buttons inside the UIAlertView I am trying to navigate to main page and previous page according to conditions. But it's getting crashed after UITableView is getting empty and I push the delete button with "Program received signal: “SIGABRT".
My code looks like this:
- (IBAction)DeleteButtonAction:(id)sender
{
DMSAppDelegate *d = (DMSAppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"Message From Custom Cell Received");
if(d->newtableData.count != 0)
{
NSIndexPath *indexPath = [self.tablevieww2 indexPathForCell:(UITableViewCell *)[[[sender superview] superview] superview]];
NSUInteger row = [indexPath row];
[d->newtableDataQt removeObjectAtIndex:row];
NSLog(#"data removed");
[self.tablevieww2 reloadData];
}
else
{
UIAlertView *alertview=[[UIAlertView alloc] initWithTitle:#"hello" message:#"Warning!!: Table is empty" delegate:self cancelButtonTitle:#"Yes" otherButtonTitles:#"",#"No",nil];
textfieldQty1 = [alertview textFieldAtIndex:0];
textfieldQty1.keyboardType = UIKeyboardTypeNumberPad;
textfieldQty1.keyboardAppearance = UIKeyboardAppearanceAlert;
textfieldQty1.autocorrectionType = UITextAutocorrectionTypeNo;
[alertview show];
[alertview release];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0)
{
DMSViewController *bt=[[DMSViewController alloc]initWithNibName:nil bundle:nil];
bt.modalTransitionStyle=UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:bt animated:YES];
}
else if (buttonIndex == 1)
{
NSString *newqty = [[NSString alloc] initWithFormat:#"%#",textfieldQty1.text];
DMSAppDelegate *d= (DMSAppDelegate *)[[UIApplication sharedApplication] delegate];
[d->newtableDataQt replaceObjectAtIndex:slCell1 withObject:(id)newqty];
NSLog(#"tb%#",d->newtableDataQt);
[self.tablevieww2 reloadData];
int total1=0;
for ( int i=0 ; i < [d->newtableDataQt count];++i )
{
NSString *string = [d->newtableDataQt objectAtIndex:i];
NSLog(#"string%#",string);
if ([string isEqualToString:#"0"])
{
}
else
{
NSLog(#"newArray%#",d->newtableDataPrice);
NSString *strP=[d->tableDataPrice objectAtIndex:i];
NSInteger sp=[strP integerValue];
NSInteger st=[string integerValue];
total1=total1+st*sp;
NSLog(#"total1%d",total1);
}
}
NSString *newtotal1=[NSString stringWithFormat:#"%d",total1];
DMSAppDelegate *d2 = (DMSAppDelegate *) [[UIApplication sharedApplication] delegate];
d2->totalD = [[NSString alloc] initWithString:newtotal1];
}
}
Please give me some solution. I am trying really hard from yesterday but not getting any success.
Thanks in advance.
#
You need to check two things:-
First:- if(d->newtableData.count != 0)
is the condition and you are not removing the items from newtableData you are removing it from newtableDataQt so thats why your else method is not getting called. because newtableData will never have count =0.
Second thing;-
one thing if your table is empty means that newtableDataQt will contain no values , it will be empty.Now when you click on the delete button, the alert view appears , after that if you click whatever button at index 1 then in your code you have written :-
[d->newtableDataQt replaceObjectAtIndex:slCell1 withObject:(id)newqty];
so newtableDataQt has already be empty before and now you are using it.This might be the reason of crash.
try
if( [newtableDataQt count] >slCell1)
{ [d->newtableDataQt replaceObjectAtIndex:slCell1 withObject:(id)newqty];
}
I hope it might help you.
I'm using this code to delete a UITableViewCell, but when I swipe to delete, it is showing the minus sign on the right first, then deleting.
I took a video to help illustrate my point: Video on YouTube
- (void)setEditing:(BOOL)editing animated:(BOOL)animate
{
[self.tableView setEditing: !self.tableView.editing animated:YES];
if (self.tableView.editing)
[self.navigationItem.leftBarButtonItem setTitle:#"Done"];
else
[self.navigationItem.leftBarButtonItem setTitle:#"Edit"];
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
PFObject *routine= [self.routineArray objectAtIndex:indexPath.row];
[routine deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
[self.routineArray removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
// [MKInfoPanel showPanelInView:self.view type:MKInfoPanelTypeError title:#"Routine Deleted" subtitle:#"You have succesfully deleted the routine!" hideAfter:2];
} else {
NSLog(#"%#", error);
}
}];
}
}
Edit:
- (void)loadData
{
PFQuery *query = [PFQuery queryWithClassName:#"Routine"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.routineArray = [objects mutableCopy];
[self.tableView reloadData];
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
-(void)addRoutine
{
PFObject *routine = [[PFObject alloc] initWithClassName:#"Routine"];
[routine setObject:self.entered forKey:#"name"];
[routine saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
[self loadData];
} else {
// There was an error saving the routine.
}
}];
}
It looks like there are two issues. The first it looks like -deleteInBackgroundWithBlock: is taking a noticeable amount of time to execute it's block after the delete button is pressed. You can try deleting the dataSource object and tableView row before deleting the data from the core data store if you aren't using a NSFetchedResultsController
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
PFObject *routine = [self.routineArray objectAtIndex:indexPath.row];
[self.routineArray removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[routine deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
//[MKInfoPanel showPanelInView:self.view type:MKInfoPanelTypeError title:#"Routine Deleted" subtitle:#"You have succesfully deleted the routine!" hideAfter:2];
} else {
NSLog(#"%#", error);
}
}];
}
}
You can also use a different animation if you prefer something other than fading out the opacity of the row. If you are targeting iOS 5.0 only, you can use UITableViewRowAnimationAutomatic to have UIKit attempt to choose the best looking animation given the circumstances.
The other issue looks like editing mode is turned back on after delete is pressed. You shouldn't need to override -setEditing:animated: so try removing that method completely.
In your -viewDidLoad: you can do the following to get editing behavior for free:
self.navigationItem.leftBarButtonItem = self.editButtonItem;
See:
An Example of Deleting a Table-View Row
UIViewController Class Reference: -editButtonItem
It should also be noted that when you are checking the editing status, you should use the isEditing accessor.
To avoid calling -reloadData, you just add the single new object to your dataSource array, then add a tableView row, then save it to the core data store. This is simply the opposite of what you had to do when deleting a row from the table view. This will work if you need to append the routine object to the end of the tableView and there is no custom sort order. Otherwise, you must insert the routine object into self.routineArray at the desired index and then create the proper indexPath to insert the tableView row at the desired location within the tableView.
- (void)addRoutine
{
PFObject *routine = [[PFObject alloc] initWithClassName:#"Routine"];
[routine setObject:self.entered forKey:#"name"];
[self.routineArray addObject:routine];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:([self.routineArray count]-1) inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath withRowAnimation:UITableViewRowAnimationAutomatic
[routine saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
[self loadData];
} else {
// There was an error saving the routine.
}
}];
}
I've a simple applications that lets you create groups of people form persons in your AddressBook... So Groups and Persons are in a one-to-many relationships, since a Group can have multiple persons. That's not a many-to-many since I create my own model of Person.
Adding data works without problems.
Deleting data doesn't. If I create a new Person, I must restart the app to delete it or the to delete the Group that Person belongs. Otherwise I get a "EXC BAD ACCESS" in the console. With NSZombieEnabled in the enviroment I get -[CFString release]: message sent to deallocated instance 0x75140d0.
I start with the CoreData stuff automatically created by XCode, create the RootViewController (subclass of TableViewController), I pass it the context and put it in a NavigationController.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Creo il controller root e gli passo il context
RootViewController *rvc = [[RootViewController alloc] initWithStyle:UITableViewStylePlain];
rvc.context = [self managedObjectContext];
//Creo il navcon, gli associo il root e lo rendo visibile
navCon = [[UINavigationController alloc] initWithRootViewController:rvc];
[window addSubview:navCon.view];
[window makeKeyAndVisible];
[rvc release];
return YES;
}
The RootViewController shows the Groups, then clicking on a row lets you modify persons in that group, passing the "nuovogruppo" (the Group Model associated with that row)
- (void)showPersoneControllerWithGruppo:(Gruppo *)nuovogruppo {
PersoneController *pc = [[PersoneController alloc] initWithStyle:UITableViewStylePlain];
pc.gruppo = nuovogruppo;
pc.context = self.context;
pc.delegate = self;
//NSLog(#"%#",[gruppi objectAtIndex:indexPath.row]);
[self.navigationController pushViewController:pc animated:YES];
[pc release];
}
And this is how I delete the person (gruppo is the Group model these persons belong to, persone is an array filled with these persons on viewDidLoad, removeGPObject is an accessor method generated by XCode (Group to Persons relationship))
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[gruppo removeGPObject:[persone objectAtIndex:indexPath.row]];
[persone removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
NSError *error;
[context save:&error];
}
}
I hope someone can help me...
UPDATE
Since I was having errors about messages sent to already released instances I tried commenting out all the [... release] lines and finally find out what was causing the problem. The problem was in the creation method of the record and not in the deleting method. Here is the method I use to create it.
The line that was causing the roblem is [NomeCognome release]
I'd be very grateful if someone could explain me why this line crashes the app.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
if (property == kABPersonPhoneProperty) {
ABMultiValueRef phoneProperty = ABRecordCopyValue(person,property);
NSString *phone = (NSString *)ABMultiValueCopyValueAtIndex(phoneProperty,identifier);
NSString *firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *surname = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
NSString *NomeCognome = [NSString stringWithFormat:#"%# %#", firstName, surname];
[firstName release];
[surname release];
Persona *persona = (Persona *)[NSEntityDescription insertNewObjectForEntityForName:#"Persona" inManagedObjectContext:context];
persona.ABRR = phone;
persona.NomeCognome = NomeCognome;
[phone release];
[NomeCognome release]; //This line makes the app crash!!! Why???
[gruppo addGPObject:persona];
NSError *error;
[context save:&error];
[self.delegate PersoneControllerDidSave:self];
[self loadContentAndReload:YES];
[self dismissModalViewControllerAnimated:YES];
}
Why that line crashes the app?
Use reloadData method at the end as the following.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[gruppo removeGPObject:[persone objectAtIndex:indexPath.row]];
[persone removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
NSError *error;
[context save:&error];
[tableView reloadData];
}
It may work now.