Refreshing a UIViewController from a Modal Controller - ios

I have a view controller which displays a carousel control (iCarousel). The view is rendered correctly and the carousel is displayed. Right after that a modal is displayed which allows the user to agree to certain terms. I want that once they agree I refresh the viewcontroller which contains the carousel control. Basically, I want to rotate the carousel to some random index.
- (IBAction)accept:(id)sender
{
NewsViewController *newsViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"NewsStoryboard"];
[newsViewController loadNews];
[newsViewController.view setNeedsDisplay];
[self dismissViewControllerAnimated:YES completion:nil];
}
The above code does call the loadNews and fetches it but the view is never refreshed.

What happens to the carousel should really be up to the view controller that manages it, not the modal view controller. Make the modal controller do its thing and return whatever data it collects to its parent. The parent (in this case, the carousel's controller) can then look at that data and decide what it needs to do next (refresh, for example).

The problem is this line:
NewsViewController *newsViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"NewsStoryboard"];
That is not the old view controller; it is a new, unused copy of that view controller. You need to create a line of communication from the modal view controller back to the existing view controller.
The typical way to do this is through a delegate, which you set when creating the modal view controller. If you look at the Xcode Utility template, you will see that it illustrates this architecture. The original view controller sets itself as the modal view controller's delegate, and the modal view controller is thus able to talk back to the original view controller as it is dismissed.
This is such an important thing to be able to do that I talk about it at length in my book:
http://www.apeth.com/iOSBook/ch19.html#_presented_view_controller

Related

Dismiss View After Switching Segmented Control To Different View?

I have three view controllers that use a segmented control to switch between them using modal segues. From what I can tell, the more a user switches between them, the more memory the app chomps up because it keeps adding new views without discarding the previous ones.
To fix this, I tried adding dismissViewControllerAnimated:YES to the end of my switching method. This resulted in a warning - "Warning: Attempt to dismiss from view controller while a presentation or dismiss is in progress!"
Any ideas how to do this so that it functions similar to a tab bar?
- (IBAction)switchTab:(id)sender {
UISegmentedControl *segControll = (UISegmentedControl *)sender;
if (segControll.selectedSegmentIndex==2)
{
[self performSegueWithIdentifier:#"segToSecondView" sender:keyValue];
}
else if (segControll.selectedSegmentIndex==0)
{
[self performSegueWithIdentifier:#"segToThirdView" sender:keyValue];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Solution: Don't use modal presentation. Create your own custom container view controller, and use the method
transitionFromViewController:toViewController:duration:options:animations:completion:
to switch between child view controllers.
You should be able to use a container view controller and an embed segue to hook up your "root" view controller (The first view controller displayed as a child) and then user the above method to switch to different children.
You'd make the container view slightly smaller than the screen so the parent view controller had room to display it's segmented control.
If you want to use a segmented control to move between controllers, you're going to need to use different logic from what you have in your question. If you're selecting an index that's greater than the one you're currently on, you want to present the view controller, if you're going to an index that's less than the current one, you should dismiss or use unwind segues. If you need to go back more than one controller, then using an unwind segue might be the best way to go.

How does the memory management work for creating view controllers on a navigation controller

I have an app that uses a navigation controller. The views display results from picking a ranom card from a deck. The logic of what card was picked and what options to use is determined in the OnCreate of each view.
After I go through the views then I go back to the root view. Now when I go back through the vues, the same information is being shown and oncreate is not being called. It seems like when I go back to the root views, the vues that get popped off memory are not being freed, so it is using the same object. Is this how its suppose to work, or am I doing something wrong so popToRootViewController is not releasing the meory the views where using?
I'm using the following code to go to the next view
if (mGet == nil) {
mGet = [[cGet alloc] initWithNibName:#"cGet" bundle:[NSBundle mainBundle]];
}
You use -viewDidLoad to perform one-time setup of your view controller at the point that all view objects are instantiated. Just before a viewController presents its view, -viewWillAppear: is executed. Just before a viewController's view goes away, -viewWillDisappear: is executed. So when you need to respond to a viewController displaying its view, or going offscreen, override -viewWillAppear: and -viewWillDisappear: in your view controller.
Check the Apple documentation for UIViewController for these details.

show another view controller but save state of current view controller

I'm trying to create a small game where the user moves over an object and displays another view controller to display information. Upon pressing a button on the recently presented view controller, the view gets dismissed and shows the view controller I started off with. I've done this like this:
ViewController.m
OtherViewController *other = [self.storyboard instantiateViewControllerWithIdentifier:#"other"];
[self presentViewController:other animated:YES completion:nil];
OtherViewController.m
[self dismissViewControllerAnimated:YES completion:nil];
but when i dismiss it, the user starts from the beginning again. is there a way to save where the user is and continue from there?
You're presenting the new view controller as a modal. When you dismiss it, the previous view controller should be uncovered in it's previous state.
If it's not, then you need to look at the logic of your ViewController class.
You want your one time setup code in your viewDidLoad method. If you have code in your viewWillAppear:animated method, or your viewDidAppear:animated method that resets the state of your view controller then that is the problem.

Objective-C: How to load a different view on button tap?

I have an iPad application that has a button on one view. When I press the button I want it to load a second view. The second view I am trying to load is a CollectionView. I am not using, and do not want to use a UINavigationController.
Does anyone know how to load a second view on a button tap? Also, I will want to create a Back button that will go back to the previous view. The previous view could be different each time the button is tapped.
There is a decent amount of material online about this topic, but I can't find anything that will work or anything that is recent.
Here is the code I have now:
-(void)showCollectionView:(id)sender
{
NSLog(#"In ShowCollectionView");
ZHCollectionViewController *cvc = [[ZHCollectionViewController alloc]
initWithNibName:#"ZHCollectionViewController"
bundle:[NSBundle mainBundle]];
[self.view addSubview:cvc.view];
NSLog(#"After all the stuff");
}
When this runs both NSLog's are executed and the message shows up in the console, but nothing happens to the view.
Yo can try to present it modally:
[self presentViewController:cvc animated:YES completion:^{
}];
Before this call you can customize appearance of your 'cvc' by defining transition and presentation styles, for example:
cvc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
cvc.modalPresentationStyle = UIModalPresentationFormSheet;
To hide it call in ZHCollectionViewController, inside some button action I believe
[self dismissViewControllerAnimated:NO completion:^{
}];
There are several ways to do this, and the way you're trying isn't one of them. If you just want to add a view, not a view controller, you should have a xib file that is a view, not a view controller. You would have to make the controller whose view you're adding this collection view into, the files owner of this collection view, so you can hook up any outlets to it.
It's not correct to add another view controller's view to your view, unless you're making that controller a child controller. If you want ZHCollectionViewController to be the controller of the collection view, then you should add that controller as a child view controller. You can check out Apple's documentation on custom container controllers to see how that's done.
You didn't really say in your question, how this collection view is to appear. Do you want it to take up the whole screen, or do you want it to be a subview? If you want it to take up the whole screen, then it would be better to just change the window's root view controller to ZHCollectionViewController, or present it modally over the current view.

Why is delegate/protocol Apple's preferred approach to dismiss presented UIViewControllers?

When a UIViewController presents another view controller the simplest way for the presented view controller to dismiss itself when it is done under iOS 5 is to call:
[[self presentingViewController] dismissViewControllerAnimated:YES completion:NULL];
On the other hand, Apple's View Controller Programming Guide says:
When it comes time to dismiss a presented view controller, the preferred approach is to let the presenting view controller dismiss it. In other words, whenever possible, the same view controller that presented the view controller should also take responsibility for dismissing it. Although there are several techniques for notifying the presenting view controller that its presented view controller should be dismissed, the preferred technique is delegation.
This has led some answers here to suggest sticking with making a new protocol and delegation even when only a very simple view controller is being presented. Why is this the documentation's "preferred technique" as opposed to the single line above? Is there any offsetting advantages to downside of a large increase in code written with the delegate/protocol technique? Obviously if there is information from the presented view controller that needs to be passed back to the presenting view controller delegation is a good technique. However, the information is the reason for delegation, not simply cleanly removing the presented view controller from the screen.
The same behavior could by achieved by [self dismissViewControllerAnimated:YES completion:nil] (before iOS 5 [self dismissModalViewControllerAnimated:YES]), as there's always at most one view controller presented (modally) at a time.
However, the point of the delegation pattern is that a single view controller could be presented in different ways such as modally or by being push to the navigation stack. That view controller doesn't know how it was presented (well, it could figure it out, but it should not care). The only thing it is supposed to do is to notify it's parent, i.e. the delegate, that its work is done. The delegate then decides how to remove the view controller (dismiss modal or pop from navigation stack etc.) or that the child should stay because the results of its work are insufficient. So the main idea is reusability of view controllers.
[[self presentingViewController] dismissViewControllerAnimated:YES completion:NULL];
That may be the simplest, but it's often not very useful.
Modal views typically need to return some information to their caller; that's why they're modal. In more traditional SDKs modal windows block their caller until the modal window is dismissed. The result of the modal window is then returned to the caller. E.g.:
int result = ShowModalDialog("Do you want to continue?");
if (result == kYes)
{
doSomething();
}
else
{
return;
}
In UIKit, -presentModalViewController: does not block, so you need some other mechanism for the modal view controller to return information to the presenting view controller. Typically that's done with delegation, though there are other ways (such as having the presenting controller handle the left and right UINavigationBar buttons).
If the modal view controller needs to return a value to its presenting view controller then that's done via delegation, and in that case it makes sense for the presenting controller to dismiss the modal controller after it has received the result. That's the original pattern.

Resources