Xcode push and pop view to save data - ios

I am trying to log data from the FirstView (one text field and one button) to SecondView (UiTableView) for example contacts list. I would like to briefly explain my code.
I have added a NavigationController with TabBar in the AppDelegate.m;
UINavigationController *pointer = [[UINavigationController alloc] init];
self.window.rootViewController = pointer;
[pointer setNavigationBarHidden:NO];
[pointer pushViewController:self.viewController animated:YES];
And I have used a [self.navigationController pushViewController:push animated:YES]; to push the data to the second screen but it always go to another NavigationController view as loop with BACK button, then I have used [self.navigationController popToRootViewControllerAnimated:YES]; which it does not pass the data to the another view. Unless I close the program completely an open it back again. So is there a way to push and pop at the same time? Or could you please show me another way?
Thanks in advance.
Note I have shared my code in this link;
Mycode

You can try to create a singleton class that manages this data. Then you just call the same singleton class throughout your code, it should keep the same state throughout the lifetime of your app.
Or, a simpler way is to Set up "myArray" as a property in your App Delegate.. Then, in your class' implementation file, do something like this:
UIApplicationDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.myArray = newArray;
this assumes myArray and newArray has been properly initialized.

Related

Manually Instantiate ViewController and push on top. Good practice?

What i'm doing is this:
UIViewController *rootController = [[[UIApplication sharedApplication] keyWindow] rootViewController];
AlarmRingViewController *alarmController = [[AlarmRingViewController alloc] init];
[rootController presentViewController:alarmController animated:YES];
What i want to achive with this, is to push my AlarmRingViewController on top of any other controller which is displaying at the moment and it works so far.
Now im wondering if this is good practice:
to instatiate a new viewController each time it should be presented
do so in a non UI related class? (in my case a scheduler for NSTimer)
from there push the newly created viewController with the rootViewController on top
Or does this violate the MVC pattern or Apples guidelines or anything.
cheers
Personally I think it's ugly code. It's hard to read and hard to debug. Split the code up a bit:
UIViewController *rootController = [[[UIApplication sharedApplication] keyWindow] rootViewController];
AlarmRingViewController *alarmController = [[AlarmRingViewController alloc] init];
[rootController presentViewController:alarmController animated:YES];
There is no benefit to typing so much into one line.
Update: Based on your updated question:
There is no problem instantiating a new view controller each time you need it. This is very common. It might be appropriate to create one and cache it. This is an optimization that could make sense if only one of the view controllers is every shown at any given time, the view controller is used very often, and it takes a lot of time to create.
View controllers are usually created and presented by other (view) controllers.
Why don't you just use a UINavigationController as the root view controller. That way, you can just do this:
AlarmRingViewController *alarmController = [[AlarmRingViewController alloc] init];
[self.navigationController presentViewController:alarmController animated:YES completion:nil];

iOS Tabs speaking to each other

I have an app where there are two basic functions, delineated by being on separate tabs on a UITabBarController. Each tab has a particular button that when pressed should, as a separate function to its normal activity, also asynchronously tell the other tab to nil its data. How can I access one tab from the other?
This would be a good use for notifications (as in NSNotification, not local or push notifications).
You should have some sort of model for each view controller in each tab. Each model can publish its notification while registering for the other. This way, neither view controller or model needs to actually know about the other directly.
When the user taps a button, the view controller tells its model to publish its notification. The other one will get the notification and act accordingly.
See the docs for NSNotificationCenter and NSNotification for details.
Definition of "tabs" for UITabBarController
// define controllers for each tab
UIViewController *viewController1 = [[UIViewController alloc] init];
UIViewController *viewController2 = [[UIViewController alloc] init];
// define tab bar controller, "self" is a UITabBarController
self.viewControllers = [NSArray arrayWithObjects: viewController1, viewController2, nil];
From this point, if you need to access a particular tab, you want to do so via the "viewController1" or "viewController2" objects. Each of these UIViewController objects would presumably have access to certain data in your application.
You can access each of your viewControllers from TabBarController's viewControllers property, and iterate through them. Zero all but the live one (self).
Put it into a GCD dispatch queue for asynchronicity.
- (IBAction)pushButton:(id)sender {
NSLog (#"%# %#",self,NSStringFromSelector(_cmd));
//do normal stuff here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (id viewController in [[self tabBarController] viewControllers]) {
if ((viewController != self)
&& ([viewController respondsToSelector:#selector(zeroData)])) {
[viewController performSelector:#selector(zeroData)];
}
}
});
}
- (void) zeroData
{
NSLog (#"%# %#",self,NSStringFromSelector(_cmd));
//each view controller should zero out it's own data
//in a method called "zeroData"
}
If you try this and look at the logs, you will see that it leaves the current vc alone but sends zeroData to the others...
Are your tabs core-data driven? If you use a NSFetchedResultsController then you get notifications for free through the NSFetchedResultsControllerDelegate protocol. Otherwise, NSNotification like rmaddy suggests.

UINavigation pushing a new root controller

I am trying to push a new root controller to a navigation stack, but using a side reveal menu.
My app delegate has the following:
welcomeViewController = [[MyWelcomeViewController alloc] initWithNibName:#"MyWelcomeViewController" bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:welcomeViewController];
navController.navigationBarHidden = YES;
// Then we setup the reveal side view controller with the root view controller as the navigation controller
self.revealSideViewController = [[PPRevealSideViewController alloc] initWithRootViewController:navController];
[self.revealSideViewController setDirectionsToShowBounce:PPRevealSideDirectionNone];
[self.revealSideViewController setPanInteractionsWhenClosed:PPRevealSideInteractionContentView | PPRevealSideInteractionNavigationBar];
// Then we make the window root view controller the reveal side view controller
self.window.rootViewController = self.revealSideViewController;
Once the welcome view controller is displayed, the user logs in. Once logged in the following process runs again from the App Delegate.
self.navController.navigationBarHidden = NO;
[self.navController setTitle:#"Home"];
[self.navController pushViewController:homeViewController animated:NO];
I then have a side view controller setup which is a table view with custom cells setup.
When a row is selected I need to push a new root controller onto the navigation controller. I try this by using the following in the table view for the cell selected.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[self.navigationController setViewControllers:[NSArray arrayWithObject:accountViewController] animated:NO];
Unfortunately this does not do anything. If I add the code to the App Delegate and then call the method from the table view controller then it works, however not from the .m file for the table view itself. Adding a log I can see the above is run, just does not do anything.
I am unsure if I need to do anything different on the above. For example, completely pop the views currently shown, then create the navigation controller and PPRevealSideViewController all over again. If I am supposed to, I am unsure how to pop all the current views to then push the new to the window, not from the AppDelegate.
The reason I do not want this in the App Delegate is because it is the incorrect way to approach this, and I would then need a separate method for each new root controller I would like to push from the menu, so the App Delegate would become very large.
Check UINavigationController.h:
#interface UIViewController (UINavigationControllerItem)
#property(nonatomic,readonly,retain) UINavigationController *navigationController; // If this view controller has been pushed onto a navigation controller, return it.
It means when you do myViewController.navigationController you will either get nil if myViewController is not pushed to any navController or the navController reference myViewController is pushed into.
As I understand your tableViewController is not pushed into the navController stack, that means you can't get the navController with tableViewController.navigationController. Instead you'll need to use anyViewControllerInTheStack.navigationController or if the navController is the rootViewController of your keyWindow, by
((UINavigationController*)[[UIApplication sharedApplication] keyWindow].rootViewController)
Add something like this to your AppDelegate.h:
#define XAppDelegate ((AppDelegate *)[[UIApplication sharedApplication] delegate])
Now you can access any iVar of AppDelegate from any .m file in your project.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[XAppDelegate.navController pushViewController:accountViewController animated:NO];
Make sure you add the correct imports.
One more thing: It's good to pop the login window from your navcontroller once you are done Logging in.
Hope this helps.

Presenting modalViewController is dealloc'ing the one calling it

I'm having a few problems integrating Greystripe adverts (documented here but not important). A way around my problem if to just present my gameView like this
iSlideAppDelegate *appDelegate = (iSlideAppDelegate *)[[UIApplication sharedApplication] delegate];
UIStoryboard *storyboard = self.storyboard;
Game3ViewController *gameView = (Game3ViewController *)[storyboard instantiateViewControllerWithIdentifier:#"mainGameController"];
[appDelegate.window addSubview:gameView.view];
This is basically just adding my gameView on top of the current view controller.
Now from this view I want to show another modal view like this
FullScreenSelfAdViewController *adView = [[FullScreenSelfAdViewController alloc] initWithNibName:#"FullScreenSelfAdViewController" bundle:nil];
[adView setDismissDelegate:self];
[self presentModalViewController:adView animated:YES];
The problem is, when this view is displayed, my gameView deallocates. Meaning when I call to dismiss the AdView it dealloc's but stays on screen as the underlying gameView isn't there any more.
Is there a better way to call these views? (I can't call my gameView by presenting it as a modal view).
Or a way to keep the gameView from deallocating?
If I had to guess, you don't have a strong variable retaining the class that gets dealloced. Whoever creates that class should have a strong ivar doing so. I had this exact same problem when I tried to create an object and use it without retaining it. Note that for UIAlerts, the system retains it when you call [alert show], leading to think you can do this with other view controllers (which you cannot!)
Good luck.

How to use a UISplitViewController as a Modal View Controller?

I am trying to display a UISplitViewController presenting it as a Modal View Controller in my iPad app. I manage to have it display, but for some reason there is a gap to the left of the modal view the size of a Status Bar which is also preserved when the orientation is changed.
Does anybody know why this is happening? Or if this is even possible? Maybe I'm just digging myself a huge hole.
Like for many of you, I needed a 'modal way' to use the UISplitViewController. This seems to be an old issue, but all I found in StackOverflow was at best an explanation why the problem happens when you attempt to do so (like the accepted answer above), or 'hack-arounds'.
However, sometimes it is also not very convenient to change much of your code-base and make a UISplitViewController the initial object just to get it's functionality up and running.
In turns out, there's a way to make everybody happy (including Apple guidelines). The solution that I found best, was to use the UISplitViewController normally, but when needed to be shown/dismissed, use the following approach:
-(void)presentWithMasterViewController: (UIViewController *) thisMasterViewController
andDetailViewController: (UIViewController *) thisDetailViewController
completion:(void(^)(void))completion
{
masterViewController = thisMasterViewController;
detailViewController = thisDetailViewController;
[self setViewControllers:[NSArray arrayWithObjects:masterViewController, detailViewController, nil]];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
self.window.rootViewController = self;
[self.window makeKeyAndVisible];
if(completion)
completion();
}
-(void)dismissViewControllerWithCompletion:(void (^)(void))completion
{
self.window = nil;
masterViewController = nil;
detailViewController = nil;
if(completion)
completion();
}
Where "window", is a property of your UISplitViewController subclass. And the system will take care of the rest!
For convenience/reference, I uploaded this as a UISplitViewController subclass to gitHub:
ModalSplitViewController
--EXAMPLE ON HOW TO USE --
mySplitViewController = [[ModalSplitViewController alloc] init];
mySplitViewController.delegate = self;
[mySplitViewController presentWithMasterViewController:masterViewController andDetailViewController:detailViewController completion:nil];
// when done:
[mySplitViewController dismissViewControllerWithCompletion:nil];
mySplitViewController = nil;
Side-note: I guess most of the confusion originates from the fact that
the UISplitView usage example from Apple documentation uses the window
created in the appDelegate, and for the fact that most people are not
so familiar with the window concept - because we normally don't need
to (they are created once in StoryBoards or boilerplate code).
Additionally, if you are doing state restoration, one should not
forget that programmatically-created UIViewControllers won't
automatically be restored by the system.
The stock UISplitViewController was designed for use as the root view controller only. Presenting one modally goes against the Apple Human Interface Guidelines and has a high probability of getting rejected by the App Review Team. In addition, you may receive the error:
Application tried to present Split View Controllers modally
Technically, this is what I did:
1/ Subclass a UIViewController ie. #interface aVC: UIViewController
2/ In the viewDidLoad, set up a splitViewController, ie. aSplitVC
3/ Then self.view = aSplitVC.view
After all, present aVC as modalViewController
I agree with Evan that this is slightly off-color for Apple, but I was able to complete a working version of this with the following solution:
UISplitViewController *splitVC = [[UISplitViewController alloc] init];
splitVC.delegate = VC2;
splitVC.viewControllers = [NSArray arrayWithObjects:navcon1, navcon2, nil];
UINavigationController *splitNavCon = [[UINavigationController alloc] init];
splitNavCon.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[splitNavCon.view addSubview:splitVC.view];
VC2.splitParentViewController = splitNavCon;
[self presentViewController:splitNavCon animated:YES completion:nil];
This allowed me to have a working back button in the new UISplitViewController that was presented modally on the screen.
You'll notice that I actually pass the VC2 (the delegate of the UISplitViewController) its parent UINavigationController. This was the best way that I found I could dismiss the UISplitViewController from within the VC2:
[splitParentViewController dismissViewControllerAnimated:YES completion:nil];
I believe one can do the other way around: instead of custom controller presenting split controller, one can set up the split controller as the root window controller in storyboard, and on top of its view you can add your custom controller (ie, login screen) and remove it from the screen (removeFromSuperview for example) when it is needed.
That answer is not actually correct, because it not valid any longer since iOS8 and if you need to support even iOS7 you can do that like you put actually modally UIViewController which has a container as SplitView.
let mdSplitView = self.storyboard?.instantiateViewControllerWithIdentifier("myDataSplitView") as! MyData_SplitVC
self.addChildViewController(mdSplitView)
mdSplitView.view.bounds = self.view.bounds
self.view.addSubview(mdSplitView.view)
mdSplitView.didMoveToParentViewController(self)

Resources