I have a page that checks if there is a list of employees on the bundle, and if there is it displays them in a table view. But if there is no list on the bundle it throws up a modal view controller. That then requires someone to login, the login is authenticated and then data is downloaded.
The ModalView is setup to be a delegate of the first page. I can call the delegate method just fine and pass the list, but when the ModalView is dismissed the table does not reload the tableview with the data. If i run the project again it loads the list up in the table view instantly.
Here is the method on the ViewDidLoad of the first page
[self checkLastLoginDate];
[self loadDataIntoArray];
if (currentData && dataLoadedIntoArray) {
NSLog(#"sweet the data is current");
[self createIndexedArray];
}
else{
NSLog(#"Data not loaded and not current");
//push login view
///if ipad do this
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
//the above code was working fine. I am just testing pushing a dynamic xib
LoginViewControlleriPad *loginControlleriPad = [[LoginViewControlleriPad alloc] initWithNibName:#"LoginViewControlleripad" bundle:nil];
loginControlleriPad.modalPresentationStyle = UIModalPresentationFormSheet;
loginControlleriPad.delegate = self;
[self presentModalViewController:loginControlleriPad animated:YES];
[self createIndexedArray];
[loginControlleriPad dismissModalViewControllerAnimated:NO];
[self.tableView reloadData];
}
The first two methods check to make sure the data exists on the bundle and that the last login date is within 15 days.
if it is then I create an IndexedArray and it displays nicely.
If the data is not on the bundle or the date is to old I check the device and use a modal view. The delegates are set and the ModalView Appears.
In the ModalView I use a syncronus and asyncronus request to hit a server for the required information. Then I create the list and pass it back to the first page.
Once the connection is made and we do a little work on the list we save it to the bundle.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:kEMPLOYEE_PLIST];
/////write to file/////
[cleanResults writeToFile:path atomically:YES];
employeeList = [[NSMutableArray alloc ]initWithContentsOfFile:path];
if (employeeList != nil)
{
[delegate passUserInfo:employeeList];
//cant get login view to dismiss
//[self dismissModalViewControllerAnimated:YES];
}
Once I get the list I right it to file and then read it back in. If there is info in the list I call the delegate method.
UPDATE
Here is the delgate method
- (void)passUserInfo: (NSMutableArray *)employeeDataArray
{
employeeData = [[NSMutableArray alloc] initWithArray:employeeDataArray];
[self.tableView reloadData];
/////FIX////////
[createIndexedArray];
}
Once that is called nothing happens. I tried dismissing the modal view, which works, but then the first page does not refresh and my app gets lost in the oblivion. I am just confused at were it is going and how i can refresh the tableview on the first page.
Is there a step I are missing? I have tried tracking it and lose it after this step.
I actually figured out that my code was setup right, i just needed to call a method that sorted the array into an alphabetic array. Once i called that method from the delegate method it worked like a snake charm.
UPDATE
I had a method that created an indexed array. When i called that inside the delegate method it reloaded the page and inserted the new into the table view. I am pretty sure it was reloading the whole time, i was just not calling the method that was populating the array that was being being displayed in the tableview.
Related
I have an entity called Practice and I use a View Controller called SelectorViewController to select one of the practices, selectedPractice. I then return selectedPractice to a view Controller called RegularViewController where I display some of the selectedPractice attributes. All of this works fine. However the app has a number of other View Controllers which can be reached by modal segues from instances of RegularViewController. As a result, if I leave and then come back to RegularViewController, selectedPractice is reset as null. I would also like to save selectedPractice so that it is available at app initialisation if it has previously been set in SelectorViewController. How do I achieve this by making selectedPractice persistent across the app, and available at runtime?
Regards
Thanks to the post above, which was great, I managed to sort it. Here is my code, which may be very clumsy, but it works.
Firstly, as I loaded the fetchedObjects into a PickerView in SelectorView Controller, I set an attribute "isSelectedPractice" to "NO" with the following code:
for (Practice *fetchedPractice in [self.fetchedResultsController fetchedObjects]) {
[fetchedPractice setValue:#"NO" forKey:#"isSelectedPractice"];
[self.managedObjectContext save:nil];
I then identified for the selected Practice:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
Practice *practice = [[self.fetchedResultsController fetchedObjects] objectAtIndex:row];
self.selectedPractice = practice;
NSLog(#"The '%#' practice was selected using the picker", self.selectedPractice.name);
}
as the view Segue'd back to RegularViewController I set the isSelectedPractice attribute for selectedPractice to YES. I kept it this late as I didn't want more than one selection in the PickerView to result in multiple objects with isSelectedPractice YES.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SavedPractice Segue"])
{
[self.selectedPractice setValue:#"YES" forKey:#"isSelectedPractice"];
[self.managedObjectContext save:nil];
NSLog(#"Setting SelectedPractice as '%#' in RegularViewController with isSelectedPractice as '%#'",self.selectedPractice.name,self.selectedPractice.isSelectedPractice );
RegularViewController *rvc= segue.destinationViewController;
rvc.delegate = self;
rvc.selectedPractice = self.selectedPractice;
}
else {
NSLog(#"Unidentified Segue Attempted!");
}
}
I then set the following Predicate in the setupFetchedResultsController method of RegularViewController:
request.predicate = [NSPredicate predicateWithFormat:#"isSelectedPractice = %#", #"YES"];
Many thanks for the help
Without seeing your actual project, one way I know will work but might be a little too round a bout would be to add an attribute "isSelectedPractice" to your entity. You could make it a BOOL, but I've had mixed results with BOOL's in Core Data, I prefer to just leave it as a NSString and set it to "yes" or "no". Then when you pull it down, modify it or add it to core Data as a entity with isSelectedPractice set to "yes". Then in your other controllers, do a
if (self.managedObjectContext == nil) {
self.managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
then do a fetch request to get entities with a predicate which is looking for isSelectedPractice equaling "yes". If you need actual code samples on how to do this let me know and I'll edit them in.
I have an NSPersistentDocument subclass using NSManagedObject subclasses for my data.
When a new document is opened, I do some initializing of data structures (trivial amount of populating fields). What I've noticed is that the Untitled document gets autosaved, and when the application re-opens, that document gets loaded. If the application quits, the user doesn't (by default) get prompted with the save dialog. If the window closes, the user does.
First question:
I want to call up the save dialog when the user quits the application. I don't want this Untitled document hanging around (under normal circumstances). I either want it saved or trashed.
I attempted to fill out:
- (void)applicationWillTerminate:(NSNotification *)aNotification
In order to trigger the document to be saved. Calling save: on the context at this point gives an error. From what I can tell, this is because the user hasn't yet saved the file on their own. In addition, calling [self close]; or [[self windowForSheet] close]; close the window without saving.
How can I force the save dialog to come up? How can I trash the untitled document?
Second question (no, I can't count):
Since when the application starts, there may or may not be an Untitled document to deal with, I'm trying to keep track of the state in another model. I've already found that the initial data (to which I referred earlier) is present when the Untitled document came up. My other model has some metadata, including a success flag/state for the populated data. Once the populated data is all in place and correct, the state indicates as such. Unfortunately, while my populated data is being loaded when the app starts with a pre-existing Untitled document, the metadata class is not.
Please excuse the roughness of the code, at this point, I'm mucking it up until I can see that it's working how I want before I polish it back off:
- (bool) createGameState {
NSEntityDescription* description = [NSEntityDescription entityForName:[GameState name] inManagedObjectContext:[self managedObjectContext]];
NSFetchRequest* req = [[NSFetchRequest alloc] init];
[req setEntity:description];
NSError *error = nil;
NSArray *array = [[self managedObjectContext] executeFetchRequest:req error:&error];
[req release];
req = nil;
GameState* result = nil;
if (array) {
NSUInteger count = [array count];
if (!count) {
// Create the new GameState.
DebugLog(#"Creating GameState");
result = [NSEntityDescription insertNewObjectForEntityForName:[GameState name] inManagedObjectContext:[self managedObjectContext]];
[result setIsLoaded:[NSNumber numberWithBool:NO]];
} else {
if (count > 1) {
NSLog(#"WARNING: Potentially Corrupt Game State. found: %lu", count);
}
result = [array objectAtIndex:0];
if ([result isLoaded]) {
[self variantLoaded];
} else {
// In this case, we have an aborted set-up. Since the game isn't
// playable, just refuse to create the GameState. This will
// force the user to create a new game.
return NO;
}
}
} else {
DebugLog(#"error: %#", error);
}
[game setState:result];
return result;
}
Note that array is always present, and count is always zero. No, I'm not explicitly calling save: anywhere. I'm relying on the standard auto-save, or the user performing a save.
EDIT:
I installed the Core Data Editor app. It turns out the issue isn't on saving the data, but on loading it. (Note: Due to another issue, the app saves as binary when instructed to save as XML, which causes much head banging.)
I've broken it down to the simplest code, which should pick up all objects of type GameState in an array. It retrieves none, despite there clearly being objects of the appropriate type in the saved file:
NSManagedObjectContext* moc = [self managedObjectContext];
NSEntityDescription* entity = [NSEntityDescription entityForName:#"GameState" inManagedObjectContext:moc];
NSFetchRequest* req = [[NSFetchRequest alloc] init];
[req setEntity:entity];
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:req error:&error];
Array is not null, but [array count] is 0.
At this point, I'm guessing it's something simple that I'm overlooking.
Second EDIT:
I added -com.apple.CoreData.SQLDebug 5 and saved as SQLite. The call to executeFetchRequest does not generate any debug logs. I do see the INSERT INTO ZGAMESTATE entry show up in the logs. It seems that executeFetchRequest is not getting passed to the backend.
Third EDIT (this one burns):
I created a new xcode project, using core data (as I had with the other). I copied just this one function (stubbing where necessary) and plopped a call to it in windowControllerDidLoadNib. In this new project, the code above works.
Found the problem.
I errantly was loading objects in Document's - (id) init call. Moved to windowControllerDidLoadNib (which is what I did in the test version) and it worked fine.
I'm attempting to present a UITabBarController modally using the following code:
// Declare all view controllers.
TabOne *tabOne = [[TabOne alloc] initWithNibName:#"TabOne" bundle:nil];
TabTwo *tabTwo = [[TabTwo alloc] init];
TabThree *tabThree = [[TabThree alloc] init];
// Set each view controller's delegate to self.
tabOne.delegate = self;
tabTwo.delegate = self;
tabThree.delegate = self;
// Set a title for each view controller.
tabOne.title = #"One";
tabTwo.title = #"Two";
tabThree.title = #"Three";
// Create a tab bar controller.
UITabBarController *tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:[NSArray arrayWithObjects:tabOne,tabTwo,tabThree, nil]];
// Present the tab bar controller modally.
[self presentModalViewController:tabBarController animated:NO];
// Memory management.
[tabOne release];
[tabTwo release];
[tabThree release];
This all works as expected except that I get the following warnings in the console:
Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate.
I've done some research on this and have checked that shouldAutorotateToInterfaceOrientation is implemented as follows:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
As far as I can tell, the problem is that the tab bar controller is not the root view controller, but I'm presenting this modal view some way into a deep view hierarchy. It's called from another modal view, which itself is called from a tab bar set in the application delegate.
I know this is a bit of an old chestnut, but it's got me stumped. Any thoughts?
Thanks in advance.
I've had a similar problem.
UITabBarController has some odd behavior with its orientation handling. When setting its orientation, it calls recursively into self.selectedViewController to decide whether to use one-stage or two-stage animation. That seems sensible, but the trouble is self.selectedViewController is initially nil (in particular, if you're displaying the UITabBarController modally for the first time), and that can confuse the controller. Depending on the iOS version, a nil selectedViewController will lead the UITabBarController to believe that one-stage animation is unsupported.
Try this: when you first load / initialize your UITabBarController, add the line
tabBarController.selectedIndex = 0;
I was getting this warning (including serious visual problems: the view controller was switching orientation but the status bar was not), and setting the index this way fixed the problem. The UITabBarController successfully called into its selected view controller, and detected the one-stage animation.
Background:
I've got a free app that is ad-supported. The basic idea is that when the app is launched, ad content (HTML) is downloaded from the server in the background, and only if the download was successful, then the content is being shown.
Previous solution:
I have implemented this successfully in a universal app. Originally, I loaded a NSData variable with an URL, and if it was successful, then I presented a modal view which contained an UIWebView (relevant code is in another question). The problem is that the external assets (images) don't get loaded in the original NSData request, so in a stroke of genius I thought of another way:
New solution:
The new way, and this is what I want help with, is that I'm instantiating the modal view controller (without displaying it yet), and having the web view begin loading the URL directly. When the webViewDidFinishLoad method is called, I fire a notification back to the parent which then performs the presentModalViewController.
Here are the methods which instantiate the view controller and present it modally:
- (void)launchAd
{
if ([ServerCheck serverReachable:#"openx.freewave-wifi.com" hideAlert:YES])
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(presentAdModal) name:#"AdLoaded" object:nil];
AdViewController *adView = [[AdViewController alloc] initWithNibName:nil bundle:nil];
[[adView view] awakeFromNib]; //THIS LINE IS WHAT THIS QUESTION IS ABOUT
navController = [[UINavigationController alloc] initWithRootViewController:adView];
[adView release], adView = nil;
[navController setModalPresentationStyle:UIModalPresentationFormSheet];
[navController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[[navController navigationBar] setTintColor:[UIColor colorWithRed:0.94 green:0.00 blue:0.32 alpha:1.00]];
}
else
LogError(#"Not presenting ad.");
}
- (void)presentAdModal
{
LogInfo(#"Presenting advertisement modal.");
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"AdLoaded" object:nil];
[tabBarController presentModalViewController:navController animated:YES];
[navController release];
}
And in the AdView controller:
- (void)webViewDidFinishLoad:(UIWebView *)theWebView
{
LogInfo(#"Ad content did load; we can show it.");
[timeout invalidate], timeout = nil;
[self setTitle:#"From Our Sponsor"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"AdLoaded" object:nil];
}
Now what's above does work. However, it took a long time for me to figure out how to cause the view controller delegate methods in the AdView controller to fire before calling presentModalViewController. I did this by adding [[adView view] awakeFromNib];, but I know this isn't the right way to make this happen.
So, everything above does work beautifully, effectively having the view controller and its UIWebView "preload" before displaying it. I just want to know what I should be doing instead of [[adView view] awakeFromNib]; -- I want to do it the right way.
Or is this right? I guess some folks would say "If it works, then it's right", but I know this isn't true.
I have a UITabBarController with 2 items, where each item is pointing to a view controller of type Navigation Controller.
The second item is a login page to my web service.
A User that did not logged in will see a login page -> "LoginViewController".
If a user is already logged in, a different view will be there -> "LoggedViewController".
On Startup, in my AppDelegate i am checking if the user already logged or need to login and i change the views.
if (!logged) {
LoginViewController * nextView = [[[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil] autorelease];
NSMutableArray * tabBarRootViews = [[NSMutableArray alloc] initWithArray:self.tabBarController.viewControllers];
[tabBarRootViews replaceObjectAtIndex:1 withObject:nextView];
[self.tabBarController setViewControllers:tabBarRootViews animated:YES];
} else {
// Same if the user is logged only diff view
}
When the view is presented, It overlaps the UINavigationBar.
I believe that the issue is that i am trying to replace a UINavigationController with a UIViewController, but i can't put my finger on the problem.
Any idea how to solve it? (I want to see both the UITabBar and UINavigationBar and the UIViewController in the middle)
Is that the correct way to change ViewControllers? Should i Use PresentModalView Instead?
Thanks!
I fixed it by keeping the UINavigationController as is and push a view controller according to the logged boolean.