Removing a View from same view. Is it good practice.? - iphone

I have added following piece of code in a view.
- (IBAction)accept_clicked:(id)sender {
[self.view removeFromSuperview];
self.view = nil;
}
Once accept is clicked, I have removed the own view like this. It worked fine, anyhow is it fine to do like this or It should be removed from another view(parent)?

Don't do this (with self.view) - it doesn't looks good and you might face difficult to find problems. self.view is the main view associated with an UIViewController. So this view to be visible on the screen, you must have shown it somehow: either pushing it to a UINavigationController or presenting it modally with -presentViewController:animated:completion: (IOS5+) or - presentModalViewController:animated:. Showing a view by instantiating a view controller and adding its view to the current view controller's view is not a good practice also:
//Not good
MyViewController *mvc = [[MyViewController alloc] init];
[self.view addSubView:mvc.view];
In your particular case I suppose you are showing some terms and conditions (or something similar) and have an accept and deny button, your best solution would be to present your view controller from somewhere, implement a delegate method, so the presenting view controller can have the result and then in your -accept_clicked: method to use either [self dismissModalViewControllerAnimated:YES] or [self dismissViewControllerAnimated:completion:] (IOS5+),

Related

Go to first view controller in app

I need to go to the first view in my app. I have a few views pushed onto the stack then a modal navigation controller and more views pushed onto that.
The problem I'm having is that using [[self navigationController] popToRootViewControllerAnimated:YES]; only goes back to the first view in the modal stack.
And I can't get [[self navigationController] popToViewController:.. to work because the true first view controller isn't accesible with [[self navigationController] viewControllers].
Any ideas on how to accomplish this? Thanks.
Do this:
[[self navigationController] dismissModalViewControllerAnimated:YES];
That will get you back to the VC that modally presented the navigation controller. Getting farther back after that depend on how you pushed those "few views" before the navigation controller.
Edit - explanation to get to the deepest root...
It sounds like those "few views" are on another, underlying navigation controller's stack. This can be a little tricky, because the clean way to get farther back in that stack is to have that underlying navigation controller pop to it's own root. But how can it know that the modal VC on top of it is done?
Let's call the view controller that did the modal presentation of second navigation controller VC_a. It's a modally presented navigation controller whose topmost VC is VC_b. How can VC_a know to pop to it's navigation root when VC_b modally dismisses itself?
The good answer (usually) is that VC_b decided to dismiss itself for a reason - some condition in your app/model changed to make it decide to be done.
We want VC_a to detect this condition, too. When VC_b gets dismissed, and VC_a gets a viewWillAppear message because it's about to be uncovered:
// VC_a.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (/* some app condition that's true when VC_b is done */) {
// I must be appearing because VC_b is done, and I'm being uncovered
// That means I'm done, too. So pop...
[self.navigationController popToRootViewControllerAnimated:NO];
} else {
// I must be appearing for the normal reason, because I was just pushed onto the stack
}
}
You need to do it by using the delegation pattern. Specifically, by creating a protocol that implements the delegate's respondsToSelector method.
See this post for complete details. It should be almost exactly what you are looking for. I had to do something similar, except I only needed to pop one view off the navigation stack instead of using popToRootViewControllerAnimated:.
For iOS6...
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
In AppDelegate.m class create method with bellow flow...
-(void)MethodName{//your method name
YourViewController *objViewController = [[[YourViewController alloc] initWithNibName:#"YourViewController" bundle:nil] autorelease]; ///define your viewcontroller name like "FirstViewController"
UINavigationController *yourNavigationController = [[[UINavigationController alloc] initWithRootViewController:objViewController] autorelease];
self.window.rootViewController = yourNavigationController;
}
When you want redirect on firstview just call this method from appdelegate object....

Unable to get presentViewController to work

I copied a working viewcontroller class from another project into a new project. I can't get the view to load in the new project. In the old project I used presentModalViewController. In the new I cannot get the view to load using either presentModalViewController or presentViewController
I am trying to load the present the view from my main view controller.
Here is what my main view controller interface looks like...
// ViewController.h
#import <UIKit/UIKit.h>
#import "RequestDialogViewController.h"
#interface ViewController : UIViewController <RequestDialogViewControllerDelegate> {
}
- (void)requestDialogViewDidDismiss:(RequestDialogViewController *)controller withResponse:(NSString*)response;
I am using presentModalViewController like this...
RequestDialogViewController *requestIPViewController = [[RequestDialogViewController alloc] initWithNibName:#"RequestDialogViewController" bundle:nil];
navigationController = [[UINavigationController alloc] initWithRootViewController:requestIPViewController];
[self presentModalViewController:navigationController animated:YES];
and presentViewController like this...
RequestDialogViewController *requestIPViewController = [[RequestDialogViewController alloc] initWithNibName:#"RequestDialogViewController" bundle:nil];
[self presentViewController:requestIPViewController animated:YES completion:nil];
What am I missing in the new project? The init method fires, but viewDidLoad does not and nothing is displayed.
Thanks
If ViewController is the root view controller, it can't present a modal view controller from within its own viewDidLoad, because at that point it doesn't have information like the screen size.
If other view controllers have already displayed, this will work. If the root view controller is a UINavigationController, you will see a view sliding in from the right while the modal view slides up from the bottom.
Anyway, for your ViewController, the soonest you could present it is after it has become visible. Using a timer for this is unreliable; older and slower devices have dramatically longer load times.
For more reliability, implement viewDidAppear: for ViewController. Do still use your timer system to add an additional delay; a fraction of a second should be sufficient. Although presenting the modal view controller from within viewDidAppear worked for me in the iOS 5.1 simulator, Presenting a modal view controller when loading another ViewController says it sometimes doesn't happen.
I have it resolved. I was trying to present the view from view did load of the main view controller. Not sure why it does not work there, but instead I am now setting a timer which calls a method to present the view controller after the main view loads and it works fine now using...
[self presentViewController:requestIPViewController animated:YES completion:nil];
Thanks to those who replied.
As #Dondragmer said, if you want to present your viewController in root view's viewDidLoad, it will fail.Once your viewController is ready for that, you can present your new viewController.
So, you can do that in
- (void)viewDidLayoutSubviews {
//present here
}
I encountered the same problem. But my situation is the presentViewController is called after the dismissViewControllerAnimated for another ViewController. My solution is to move the presentViewController to completion block of dismissViewControllerAnimated.
Present a modalViewController:
For the benefit of all starting programmers, type it instead of copy paste.
myVC *viewController = [[myVC alloc]initWithNibName:#"myVC" bundle:nil];
viewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:viewController animated:YES];
[viewController release];
It looks like you were trying to present a nav controller as a view controller in the first sample, then you were using the wrong method in the second one.

UIModalTransitionStylePartialCurl with UITabBarController

This question has been asked a lot e.g. here but as far as I can see is yet to be answered in full.
I have a UITabBarController with a UINavigationController as the root vc for one of the tabs, which itself has a MKMapView as its root vc. The behaviour I want is for the map to partially curl upwards, while leaving the tab bar in place (similar to the Maps app).
So far all I have managed to get working is for the whole view to curl, which isn't as nice.
Solutions I have seen are to set the hidesBottomBarWhenPushed property to NO, which would make sense however this doesn't seem to work, (unless I am doing something wrong).
For clarity, my code is as follows:
MyVC *aView = [MyVC init];
aView.modalTransitionStyle = UIModalTransitionStylePartialCurl;
aView.hidesBottomBarWhenPushed = NO;
For the presenting part, I have tried the two alternatives below, neither of which seem to work:
[self presentModalViewController:updateStatus animated:YES];
[[self navigationController] presentModalViewController:updateStatus animated:YES];
Any help much appreciated.
I've scoured StackOverflow (and the Internet) for a solution to this problem. The question has been asked many times, but as you note, never sufficiently answered. Many solutions give an acceptable solution if it is unimportant whether, e.g., a lower toolbar curls up as well.
Others have provided a solution using UIView animations / CoreAnimation rather than UIModalTransitionStylePartialCurl as a modal transition style; this is at worst a solution not allowed in the App Store, and at best is not quite the same effect as one gets from UIModalTransitionStylePartialCurl (e.g. the shape of the curl is different).
None of these solutions have provided an answer that mimics Apple's solution in the Maps app (i.e., using UIModalTransitionStylePartialCurl but leaving an un-curled UIToolbar at the bottom of the screen).
I will continue in this tradition of incomplete answers, since you ask about a UITabBarController and my solution doesn't specifically address that case. It does, however, solve the problem I had, which was to get a half page curl with an un-curled toolbar at the bottom.
There must be a more elegant way to do this, but this is how I managed.
The rootViewController of my AppDelegate is a subclass of UIViewController, which I'll call TAContainerViewController. TAContainerViewController manages a) the actual contents of the screen (the "stuff to be curled"), TAContentViewController, and b) the contents "behind" the TAContentViewController (e.g. settings), which I'll call TAUnderCurlViewController.
My instance of TAContainerViewController had properties for a TAContentViewController and a TAUnderCurlViewController. The UIView that was my content was a subview of TAContentViewController's view property; likewise what the user sees under the curl is the view property of the TAUnderCurlViewController.
In the init method of TAContainerViewController I make sure to do the following:
_underCurlVC.modalTransitionStyle = UIModalTransitionStylePartialCurl;
And to curl the contents to reveal under the page, I set up an action that calls this code:
[self.contentVC presentModalViewController:self.underCurlVC animated:YES];`
where self is the TAContainerViewController, contentVC is an instance of TAContentViewController, and underCurlVC is an instance of TAUnderCurlViewController.
To dismiss the view, simply [self.contentVC dismissModalViewControllerAnimated:YES];.
Some strangeness seems to occur with the frame of contentVC when the modal view is dismissed, so I manually reset the frame when the modal view is dismissed.
I've posted a sample project with more details on Github. Hopefully someone can take this and turn it into a slightly more elegant solution, or expand it to work with a UINavigationController or UITabBarController. I think the trick is to pull the View Controllers out of the well-defined relationships in the Cocoa subclasses, so maybe subclassing those specialty View Controllers would do it.
Tim Arnold's response worked great for me, thanks!
One trap to watch out for: your modal page-curl transition will take over the whole screen if your content view controller is added as a child of the container view controller. You could just not add it as a child, but then none of the view lifecycle methods will get called on your content controller (e.g. viewDidLoad, viewWillAppear), which could be a problem.
Fortunately, there is a way around this. In your container controller:
Add your content controller as a child in viewDidLoad
Remove it as a child in viewDidAppear
Re-add it as a child in viewWillDisappear.
That way, your content controller gets its lifecycle methods called, while still being able to do a modal page-curl transition without taking up the whole screen.
Here is the entire code of a bare-bones solution:
#interface XXContainerController : UIViewController
#property (strong, nonatomic) UIViewController *contentController;
#property (nonatomic) BOOL curled;
#end
#implementation XXContainerController
#synthesize contentController = _contentController;
#synthesize curled = _curled;
- (void)viewDidLoad
{
[super viewDidLoad];
self.contentController = [self.storyboard
instantiateViewControllerWithIdentifier:#"SomeControllerInStoryboard"];
// Add content controller as child view controller.
// This way, it will receive all the view lifecycle events
[self addChildViewController:self.contentController];
self.contentController.view.frame = self.view.bounds;
[self.view addSubview:self.contentController.view];
[self.contentController didMoveToParentViewController:self];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Remove the content controller as child view controller.
// This way, the modal page curl transition will
// not take over the whole screen.
// NOTE: need to wait until content controller has appeared
// (which will happen later).
// Achieve this by running the code at the end of the animation loop
[UIView animateWithDuration:0 animations:nil completion:^(BOOL finished) {
[self.contentController removeFromParentViewController];
}];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// Add the content controller as child view controller again
// so it receives the view lifecycle events
[self addChildViewController:self.contentController];
}
- (void)setCurled:(BOOL)curled
{
if (curled == _curled) return;
_curled = curled;
// Curl up the content view and show underneath controller's view
if (curled) {
// Note you can specify any modal transition in storyboard
// E.g. page curl, flip horizontal
[self.contentController
performSegueWithIdentifier:#"SomeModalSegueDefinedInStoryboard"
sender:self];
// Uncurl and show the content controller's view again
} else {
[self.contentController dismissModalViewControllerAnimated:YES];
// Have to do this, otherwise the content controller's view
// gets messed up for some reason
self.contentController.view.frame = self.view.bounds;
}
}
#end

why does viewDidAppear not get triggered?

i have a root view controller that inserts a subview at index 0 at its viewDidLoad method.
i am trying to get the subview to become firstResponder, but can only do this - from my understanding - in the subview's viewDidAppear method.
here's the line of code i added to the root view controller's viewDidLoad method:
[self.view insertSubview: subViewController.view atIndex: 0];
the subviewcontroller has a xib, subViewController.xib, that is shown correctly at runtime. nevertheless, the subViewController's viewDidAppear does not get triggered.
any idea why this happens? any idea how to remedy this - apart from calling viewDidAppear manually (doing so results in failure to become firstResponder)?
thanks,
mbotta
You have to push the view controller on to a navigation stack in order for it's delegate methods to get called. Adding your view controller's view to the subview array won't call them. The first thing you should do is read the View Controller Programming Guide from Apple as this will save you from some headaches you're creating by doing this in a non-standard way.
Instead of adding the view to your root view controller subviews, do this:
SubviewController *controller = [[SubviewController alloc] init];
[[self navigationController] pushViewController:controller animated:YES];
[controller release], controller = nil;
Now your delegate methods will get called. If you don't have a navigation controller as your root view controller, though, this won't work.
If I recall correctly (sorry can't find the place in docs now) -viewDidAppear does not get called for subviews. You must call it manually in the -viewDidAppear method of parent view controller.

Adding a subview into view hierarchy

I'd like to have a view appear when the user clicks a button. The hierarchy I have looks like this:
MainWindow
-UIView
--ScrollView
---ScrollView.pages = UIViews
----UIView (from above assignment)
----TextView
----InfoButton
pages is an NSMutableArry of pageController objects. These hook to a nib. These nibs are the pages that user flicks through in the scroll view.
The InfoButton click is wired up like this:
- (IBAction) infoButton_click:(id)sender{
topView topViewViewController *topView = [[topViewViewController alloc] initWithNibName:#"TopView" bundle:[NSBundle mainBundle]];
//[self.navigationController pushViewController: topViewView animated:YES];
//[self.view addSubview: topViewView.view];
[super.view addSubview: topViewView.view];
[topViewView release];
}
InfoButton is on one of the pages in the ScrollView. I've commented out different code that has been tried. None of it adds the view. Nothing happens. Is there a way to get TopView as the top view in the hierarchy?
Is your goal to add the view as a subview, or to slide on a new view using the navigation controller? I'm going to assume the latter for the moment.
- (IBAction)infoButton_click:(id)sender
{
TopViewController *topViewController = [[TopViewController alloc] initWithNibName:#"TopView" bundle:nil];
[self.navigationController pushViewController:topViewController animated:YES];
[topViewController release];
}
This is correct if you actually have a navigationController. Make sure you actually do. When "nothing happens" in Cocoa, it usually means something is nil. You should check in the debugger or with NSLog() to see if any of these values are nil. It is possible (even likely), that your parent has a navigationController, but you do not.
Classes should always have a leading capital. Do not create a variable called "view" that is of class "UIViewController". This is a sure path to suffering. Objective-C is a dynamic language with limited compiler checks on types. Naming things correctly is critical to effective programming in ObjC.
Based on your comment to a previous answer, you want to present a modal view. You do this by creating a new view "modalView" and calling [topView presentModalViewController:modalView animated:YES].
In a future version of the iPhone OS, which of course I would be unable to comment upon if it were under NDA, you might be able to present a modal view controller with a flip transition by setting a property on the view controller to be presented, which would probably be called modalTransitionStyle or somesuch.