How to reload a tableView i.e call the viewDidLoad method if a condition is met - objective-c

The problem is this i need a way to basically erase all the entry data a user placed into my arrays if a condition is met. Im new to Objective-C and iOS programming, but i believed the solution might be in calling the viewDidLoad method, thus it would virtually refresh the applications with the values of the array reset to default. If there is any other logical way of doing this i would appreciate the help.
In short i need to refresh the arrays as they were when the application first launched and the user did not select anything.
This is the part where i need it to refresh.
if ([gradeRecieved objectAtIndex:i]==nil) {
break; // if this condition is met the program must begin anew.
Edit* I need to recall the - (void)viewDidLoad method
here is more of the code.
-(IBAction)button:(id)sender{
double sum = 0;
int i = 0;
double gradeEarned=0;
double creditHours = 3;
while (i<8){
// [credits removeObjectIdenticalTo:[NSNull null]];
if ([credits count ] ==0) {
break;
}
if ([credits objectAtIndex:i] == radioButtonA) {
// [gradeRecieved replaceObjectAtIndex:i withObject:GradeA];
[defArray addObject:GradeA];
gradeEarned+=GradeA.intValue;
i++;
continue;
}
if ([credits objectAtIndex:i] == radioButtonB) {
// [gradeRecieved replaceObjectAtIndex:i withObject:GradeB];
[defArray addObject:GradeB];
gradeEarned+=GradeB.intValue;
i++;
continue;
}
if ([credits objectAtIndex:i]== radioButtonC){
// [gradeRecieved replaceObjectAtIndex:i withObject:GradeC];
[defArray addObject:GradeC];
gradeEarned+=GradeC.intValue;
i++;
continue;
}
if ([credits objectAtIndex: i] == defaulter) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Custom button pressed" message:[NSString stringWithFormat:#"You pressed the custom button C"] delegate:self cancelButtonTitle:#"great"otherButtonTitles:nil];
[alert show];
[alert release];
[self viewDidLoad];
break;
}
}
if ([defArray count]>0) {
sum= ([defArray count])/(creditHours*gradeEarned);
NSLog(#"%f",sum);}
this new code however results in the app freezing if the user clicks the button and then try's to redo some selections made

If you want to call that code again, wrap that code into a method and call that method in viewDidLoad and wherever else you want. Here is some example code:
- (void)viewDidLoad {
if ([gradeRecieved objectAtIndex:i]==nil) {
[self refreshArray];
}
}
- (void)refreshArray {
// refresh here
}
Then call [self refreshArray] wherever you need!
I see you are new! If this answer was correct you can up vote or tick!

The viewDidLoad method loads only once - when the view is first loaded. To reload data, the easiest way is to create another method and call that method everytime you need to load data.
Example,
-(void)loadingData{
//code to load the data
}
-(void)viewDidLoad{
//this will call the loadingData method to load the data
[self loadingData];
}

Related

Is it ok to perform a segue inside a for loop?

Is it ok to perform a segue in a for loop like below or I am setting myself up for trouble?
Or will it simply perform the segue and the rest of the code is never executed?
for(symbol in results) {
if ([symbol.data hasPrefix:#"--"]) {
actualBarCodeStr = [symbol.data substringFromIndex:2];
[self performSegueWithIdentifier:#"trListViewToTrSearchView" sender:self];
} else {
createTransactionResult = [NWBarCodeHelper createTransactionRowFromBarCode:symbol.data];
if ([NWTillHelper isDebug] == 1) {
NSLog(#"Zbar delegate holds barcode: %#", symbol.data);
if(createTransactionResult != 0) {
NSLog(#"TransactionListView:ZBarDelegate:createTransactionFrombarCode failed with errorCode %i", createTransactionResult);
}
}
}
}
As the code is in a method, they will all run in one runloop even if you have performed the first segue. So all the segue will be performed. If it's just a push, I think there will be a bunch of view controller be pushed. For cases like modal present, I think it may crash but I have not tested it.

Changing cocoa bindings value programmatically

I have an NSButton in my preferences to interact with adding the application to the LoginItems. If the adding of the login item fails, I want to uncheck the box so the user doesn't get a false sense that it was added to the login items. However, after doing this, when I click the checkbox again, the bindings is not triggered.
- (void)addLoginItem:(BOOL)status
{
NSURL *url = [[[NSBundle mainBundle] bundleURL] URLByAppendingPathComponent:
#"Contents/Library/LoginItems/HelperApp.app"];
// Registering helper app
if (LSRegisterURL((__bridge CFURLRef)url, true) != noErr) {
NSLog(#"LSRegisterURL failed!");
}
if (!SMLoginItemSetEnabled((__bridge CFStringRef)[[NSBundle mainBundle] bundleIdentifier], (status) ? true : false)) {
NSLog(#"SMLoginItemSetEnabled failed!");
[self willChangeValueForKey:#"startAtLogin"];
[self.startAtLogin setValue:[NSNumber numberWithBool:[self automaticStartup]] forKey:#"state"];
[self didChangeValueForKey:#"startAtLogin"];
}
}
- (void)setAutomaticStartup:(BOOL)state
{
NSLog(#"Set automatic startup: %d", state);
if ([self respondsToSelector:#selector(addLoginItem:)]) {
[self addLoginItem:state];
}
}
- (BOOL)automaticStartup
{
BOOL isEnabled = NO;
// the easy and sane method (SMJobCopyDictionary) can pose problems when sandboxed. -_-
CFArrayRef cfJobDicts = SMCopyAllJobDictionaries(kSMDomainUserLaunchd);
NSArray* jobDicts = CFBridgingRelease(cfJobDicts);
if (jobDicts && [jobDicts count] > 0) {
for (NSDictionary* job in jobDicts) {
if ([[[NSBundle mainBundle] bundleIdentifier] isEqualToString:[job objectForKey:#"Label"]]) {
isEnabled = [[job objectForKey:#"OnDemand"] boolValue];
break;
}
}
}
NSLog(#"Is Enabled: %d", isEnabled);
// if (isEnabled != _enabled) {
[self willChangeValueForKey:#"startupEnabled"];
startupEnabled = isEnabled;
[self didChangeValueForKey:#"startupEnabled"];
// }
return isEnabled;
}
I have my databinding for the checkbox bound to self.automaticStartup. If I remove the line [self.startAtLogin setValue:[NSNumber numberWithBool:[self automaticStartup]] forKey:#"state"]; then the bindings work fine, but it doesn't uncheck, if the adding of the item fails.
How can I change this binding value programmatically so that every other binding event is not ignored?
From your explanation, your bound value is automaticStartup, but you are sending willChangeValueForKey: for startAtLogin. In order for bindings to work correctly, you need to alert on the change to the bound variable at some point. However, since you are in the midst of setAutomaticStartup: at the time, it's not really safe to do that here.
In this case, I would not use bindings to perform the change itself, I would consider the old-style IBAction mechanism and then set the checkbox value manually through an IBOutlet when you can confirm the status.

NSDocument saveDocumentWithDelegate deadlocked during App termination

NSDocument continues to be a software maintenance nightmare.
Anyone else having a problem where they want certain blocking dialogs to be handled SYNCHRONOUSLY?
BEGIN EDIT: I may have found a solution that allows me to wait synchronously
Can anyone verify that this would be an "Apple approved" solution?
static BOOL sWaitingForDidSaveModally = NO;
BOOL gWaitingForDidSaveCallback = NO; // NSDocument dialog calls didSave: when done
...
gWaitingForDidSaveCallback = true;
[toDocument saveDocumentWithDelegate:self
didSaveSelector:#selector(document:didSave:contextInfo:)
contextInfo:nil];
if ( gWaitingForDidSaveCallback )
{
// first, dispatch any other potential alerts synchronously
while ( gWaitingForDidSaveCallback && [NSApp modalWindow] )
[NSApp runModalForWindow: [NSApp modalWindow]];
if ( gWaitingForDidSaveCallback )
{
sWaitingForDidSaveModally = YES;
[NSApp runModalForWindow: [NSApp mbWindow]]; // mbWindow is our big (singleton) window
sWaitingForDidSaveModally = NO;
}
}
...
- (void)document:(NSDocument *)doc didSave:(BOOL)didSave contextInfo:(void *)contextInfo
{
[self recordLastSaveURL];
gWaitingForDidSaveCallback = NO;
if ( sWaitingForDidSaveModally )
[NSApp stopModal];
}
END EDIT
I have to support Snow Leopard/Lion/ML
App termination is an ugly process.
When the user decides to quit, and the document has changes that need saving, I call this:
gWaitingForDidSaveCallback = true;
[toDocument saveDocumentWithDelegate:self
didSaveSelector:#selector(document:didSave:contextInfo:)
contextInfo:nil];
I really really really want this call to be synchronous, but in latest Lion, this hangs my app:
while ( gWaitingForDidSaveCallback )
{
// didSave: callback clears sWaitingForDidSaveCallback
// do my own synchronous wait for now
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0.05]];
}
My best guess for the hang is that the mouseDown: of a window close button
is confusing the NSDocument.
So now, I have to return, and pepper my apps main loop with unmaintainable state machine logic to prevent user from executing various dangerous hotkeys.
Ok, so I grin and bear it, and run into yet another roadblock!
In previous OS versions/SDKs, [NSApp modalWindow] would return a window when it
was in this state. Now it doesn't! Grrrrr...
NSDocument has no API to test when it is in this state!
So, now there is no mechanism to globally check this state!
I have to add yet another state variable to my state machine.
Anyone have a cleaner solution for this problem that works in all OS versions and all present (and future) SDKs?
The better way is to save unsaved documents in chain. It is very easy:
// Catch application terminate event
-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
NSDocumentController *dc = [NSDocumentController sharedDocumentController];
for (NSInteger i = 0; i < [[dc documents] count]; i++)
{
Document *doc = [[dc documents] objectAtIndex:i];
if ([doc isDocumentEdited])
{
// Save first unsaved document
[doc saveDocumentWithDelegate:self
didSaveSelector:#selector(document:didSave:contextInfo:)
contextInfo:(__bridge void *)([NSNumber numberWithInteger:i + 1])]; // Next document
return NSTerminateLater; // Wait until last document in chain will be saved
}
}
return NSTerminateNow; // All documents are saved or there are no open documents. Terminate.
}
...
// Document saving finished
-(void)document:(NSDocument *)doc didSave:(BOOL)didSave contextInfo:(void *)contextInfo
{
if (didSave) // Save button pressed
{
NSDocumentController *dc = [NSDocumentController sharedDocumentController];
NSInteger nextIndex = [(__bridge NSNumber *)contextInfo integerValue];
for (NSInteger i = nextIndex; i < [[dc documents] count]; i++)
{
Document *doc = [[dc documents] objectAtIndex:nextIndex];
if ([doc isDocumentEdited])
{
// Save next unsaved document
[doc saveDocumentWithDelegate:self
didSaveSelector:#selector(document:didSave:contextInfo:)
contextInfo:(__bridge void *)([NSNumber numberWithInteger:nextIndex + 1])]; // Next document
return;
}
}
[NSApp replyToApplicationShouldTerminate:YES]; // All documents saved. Terminate.
}
else [NSApp replyToApplicationShouldTerminate:NO]; // Saving canceled. Terminate canceled.
}
Maybe this answer is too late to be useful but... In one of my apps I implemented -(IBAction)terminate:(id)sender in my NSApplication derived class which would conditionally call [super terminate] to actually close the application only if all open documents were cleanly saved. I may have found some of this in the Apple docs or other examples.
The terminate override will go through each document and either close it (because it's saved), or call the document's canCloseDocumentWithDelegate method in the NSDocument derived class passing 'self' and 'terminate' as the didSaveSelector. Since the terminate method falls through and does nothing except make the document present an NSAlert, the alert in the document class will callback and re-run the terminate routine if the user clicks YES or NO. If all documents are clean, the app will terminate since [super terminate] will get called. If any more dirty documents exist, the process repeats.
For example:
#interface MyApplication : NSApplication
#end
#implementation MyApplication
- (IBAction)terminate:(id)sender
{
//Loop through and find any unsaved document to warn the user about.
//Close any saved documents along the way.
NSDocument *docWarn = NULL;
NSArray *documents = [[NSDocumentController sharedDocumentController] documents];
for(int i = 0; i < [documents count]; i++)
{
NSDocument *doc = [documents objectAtIndex:i];
if([doc isDocumentEdited])
{
if(docWarn == NULL || [[doc windowForSheet] isKeyWindow])
docWarn = doc;
}
else
{
//close any document that doesn't need saving. this will
//also close anything that was dirty that the user answered
//NO to on the previous call to this routine which triggered
//a save prompt.
[doc close];
}
}
if(docWarn != NULL)
{
[[docWarn windowForSheet] orderFront:self];
[[docWarn windowForSheet] becomeFirstResponder];
[docWarn canCloseDocumentWithDelegate:self shouldCloseSelector:#selector(terminate:) contextInfo:NULL];
}
else
{
[super terminate:sender];
}
}
#end
Later in the document derived class:
typedef struct {
void * delegate;
SEL shouldCloseSelector;
void *contextInfo;
} CanCloseAlertContext;
#interface MyDocument : NSDocument
#end
#implementation MyDocument
- (void)canCloseDocumentWithDelegate:(id)inDelegate shouldCloseSelector:(SEL)inShouldCloseSelector contextInfo:(void *)inContextInfo
{
// This method may or may not have to actually present the alert sheet.
if (![self isDocumentEdited])
{
// There's nothing to do. Tell the delegate to continue with the close.
if (inShouldCloseSelector)
{
void (*callback)(id, SEL, NSDocument *, BOOL, void *) = (void (*)(id, SEL, NSDocument *, BOOL, void *))objc_msgSend;
(callback)(inDelegate, inShouldCloseSelector, self, YES, inContextInfo);
}
}
else
{
NSWindow *documentWindow = [self windowForSheet];
// Create a record of the context in which the panel is being
// shown, so we can finish up when it's dismissed.
CanCloseAlertContext *closeAlertContext = malloc(sizeof(CanCloseAlertContext));
closeAlertContext->delegate = (__bridge void *)inDelegate;
closeAlertContext->shouldCloseSelector = inShouldCloseSelector;
closeAlertContext->contextInfo = inContextInfo;
// Present a "save changes?" alert as a document-modal sheet.
[documentWindow makeKeyAndOrderFront:nil];
NSBeginAlertSheet(#"Would you like to save your changes?", #"Yes", #"Cancel", #"No", documentWindow, self,
#selector(canCloseAlertSheet:didEndAndReturn:withContextInfo:), NULL, closeAlertContext, #"%");
}
}
- (void)canCloseAlertSheet:(NSWindow *)inAlertSheet didEndAndReturn:(int)inReturnCode withContextInfo:(void *)inContextInfo
{
CanCloseAlertContext *canCloseAlertContext = inContextInfo;
void (*callback)(id, SEL, NSDocument *, BOOL, void* ) = (void (*)(id, SEL, NSDocument *, BOOL, void* ))objc_msgSend;
if (inAlertSheet) [inAlertSheet orderOut:self];
// The user's dismissed our "save changes?" alert sheet. What happens next depends on how the dismissal was done.
if (inReturnCode==NSAlertAlternateReturn)
{
//Cancel - do nothing.
}
else if (inReturnCode==NSAlertDefaultReturn)
{
//Yes - save the current document
[self saveDocumentWithDelegate:(__bridge id)canCloseAlertContext->delegate
didSaveSelector:canCloseAlertContext->shouldCloseSelector contextInfo:canCloseAlertContext->contextInfo];
}
else
{
// No - just clear the dirty flag and post a message to
// re-call the shouldCloseSelector. This should be
// the app:terminate routine.
[self clearDirtyFlag];
if (canCloseAlertContext->shouldCloseSelector)
{
(callback)((__bridge id)canCloseAlertContext->delegate,
canCloseAlertContext->shouldCloseSelector, self, YES, canCloseAlertContext->contextInfo);
}
}
// Free up the memory that was allocated in -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:.
free(canCloseAlertContext);
}
#end
And that should do it - No loops... no waiting...

Dealing with multiple BOOL combinations

Here is the scenario: In my app I'm syncing some data, whenever there is some error when syncing I flag this in a BOOL. When all syncing is complete I want to display sync feedback (errors) for the user.
If there is ie a calendar sync error and a contact sync error I first display a UIAlertView with information about the calendar sync error, when the user has tapped "OK" I then display a UIAlertView with information about the contact sync error. To be able to know when the user has tapped "OK" I use completion blocks. So my code looks something like this:
if (calendarSyncFailed && contactSyncFailed && facebookSyncFailed && contactSyncConflicts) {
[self displayCalendarSyncAlertCompletionBlock:^{
[self displayContactsSyncAlertCompletionBlock:^{
[self displayFacebookSyncAlertCompletionBlock:^{
[self displayContactSyncConflictsAlertCompletionBlock:^{
}];
}];
}];
}];
} else if (calendarSyncFailed && contactSyncFailed && facebookSyncFailed) {
[self displayCalendarSyncAlertCompletionBlock:^{
[self displayContactsSyncAlertCompletionBlock:^{
[self displayFacebookSyncAlertCompletionBlock:^{
}];
}];
}];
} else if (contactSyncFailed && facebookSyncFailed && contactSyncConflicts) {
[self displayContactsSyncAlertCompletionBlock:^{
[self displayFacebookSyncAlertCompletionBlock:^{
[self displayContactSyncConflictsAlertCompletionBlock:^{
}];
}];
}];
} else if (you get the idea…) {
}
As you can see there will be alot of different combinations for dealing with these 4 boolean values and I was wondering if there is a more smarter/elegant way of coding this?
While I do agree with demosten that it would be better to have only one message, this is how I would do this with less code:
Use a mutable array as a property where you store your alertviews.
In the method where you test your conditions, create an alert view for every failure that evaluates to true, and put them in your array in the desired order. (This is the key part as you are only doing 4 tests, instead of 2^4 - 1 tests).
Implement the UIAlertViewDelegate method alertView: didDismissWithButtonIndex: something like this:
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSInteger nextIndex = [self.alertViews indexOfObject:alertView] + 1;
if (nextIndex < [self.alertViews count]){
UIAlertView *next = [self.alertViews objectAtIndex: nextIndex];
[next show];
}
}

Pausing iteration of a for loop to wait for user input

I wrote a for loop which is iterating through an array of objects.
Now I am asking myself if it's possible to break the iteration of the loop until the user clicks on a button which calls a IBAction?
for (int i = 0; i < [array count]; i++) {
// do something with the object
// wait for action method called
// user clicked action so go on
}
You can adapt the code to fit your case. It basically "unrolls" the loop into multiple messages. Start the sequence with [self doItForIndex:[NSNumber numberWithInt:0]];
- (BOOL)canDoitForIndex:(NSNumber *)i {
// return YES if you want to go ahead
// (e.g. test a BOOL you set in response to the user tapping a button
}
- (void)waitForIndex:(NSNumber *)i {
if ([self canDoItForIndex:i]) {
// do anything to clean up for i
// then repeat for i+1:
[self doItForIndex:[NSNumber numberWithInt:[i intValue]+1]];
} else {
[self performSelector:_cmd withObject:i afterDelay:0.01f;
}
}
- (void)doItForIndex:(NSNumber *)i {
if ([i intValue] < lastIndex) {
// do what you have to do
[self waitForIndex:i];
}
// else you're done
}
Apple's NSRunLoop concept expects you to complete processing pretty quickly. If you tie up the main thread by waiting for something, nothing else in your app can happen. The above code breaks the "wait" into multiple message sends, and keeps your app responsive.
ODRM algorithm works very well.
I just changed this line :
[self performSelector:_cmd withObject:i afterDelay:0.01f];
with this :
[NSThread sleepForTimeInterval:0.25];
[NSThread detachNewThreadSelector:_cmd toTarget:self withObject:i];
As I had UI elements to be updated, it was better for we to force waiting to be in a background thread.