I'm pretty new to iOS development, and I'm trying to develop a simple app in which a button changes the subviews. I have a base RootViewController, which displays MiddleView correctly on init. MiddleView has a single button, labeled "First," which is connected (in Interface Builder) to RootViewController's -openFirstView.
Here's how MiddleView is displayed within RootViewController's -viewDidLoad
MiddleViewController *middleTemp = [[MiddleViewController alloc] initWithNibName:#"MiddleView" bundle:nil];
self.middle = middleTemp;
self.middle.rootViewController = self;
[self.view addSubview:middle.view];
[middleTemp release];
So I have the following ViewControllerss: MiddleViewController and FirstController which control MiddleView and FirstView respectively, and a RootViewController which switches between the two.
I've linked this by placing a RootViewController reference in MiddleViewController, and adding
self.middle.rootViewController = self;
to RootViewController's -viewDidLoad.
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
[self.view addSubview:firstController.view];
}
Note: I've tried initializing firstController within -openFirstView, and when it initially didn't run, I moved the initialization to -viewDidLoad and have proven that it is initializing from the nib correctly by displaying FirstView directly in -viewDidLoad
Where firstController is loaded to a reference earlier in code. However, when I run the code and click the button, nothing in the view changes.
I've done some more diagnosing. I've found specifically that -ViewDidLoad in rootViewController is being called twice, once on the original load and once on the first click of the button, and I'm not sure exactly why.
It seems you haven't inialize your firstView in the below method
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
//First initialize the firstController
[self.view addSubview:firstController.view];
}
Related
As part of my updating my apps to replace the deprecated presentModalViewController with presentViewController, I did some testing.
What I found was disturbing. Whereas presentModalViewController always works and there is no question about it working, I have found the presentViewController method often will not display my VC at all. There is no animation and it never shows up.
My loadView are called without problems, but the actual view does not appear.
So here is what I am doing:
User taps a button in my main view controller.
In the callback for that tap, I create a new view controller and display it as shown above.
The VC never appears (it is an intermittent problem though) but because this VC begins playing some audio, I know that its loadView was called, which looks like as follows.
My button-pressed callback is as follows:
- (void) buttonTapped: (id) sender {
VC *vc = [[VC alloc] init];
[self presentViewController: vc animated:YES completion: nil];
[vc release];
}
Here is my loadview in the VC class:
- (void) loadView {
UIView *v = [UIView new];
self.view = v;
[v release];
... create and addsubview various buttons etc here ...
}
Thanks.
Make sure the controller that calls the function has its view currently displayed (or is a parent to the one currently displayed) and it should work.
I have 2 ViewControllers that I use App delegate to switch them according to user interaction.
in AppDelegate.m I have:
- (void) switchViews
{
if (_viewController.view.superview == nil) {
[_window addSubview:_viewController.view];
[_window bringSubviewToFront:_viewController.view];
[viewController2.view removeFromSuperview];
} else
{
[_window addSubview:_viewController2.view];
[_window bringSubviewToFront:_viewController2.view];
[_viewController.view removeFromSuperview];
}
}
_viewController is for main view and _viewController2 is for glview(I am using isgl3d). The switch works but everytime I switch back to glview, I see duplicated view on top, which I suspect even main view is duplicated too.
Any idea how can I remove the view entirely so that I don't have this issue? Thanks!
You shouldn't be adding and removing the views like this, just change which controller is the root view controller of the window. Doing that make the new controller's view a subview of the window, and removes the old controller's view.
if ([self.window.rootViewController isEqual: _viewController]) {
self.window.rootViewController = viewController2;
}else{
self.window.rootViewController = viewController;
I found out how to do this after watching Stanford Coding Together:IOS.
Some critical info of VC that I am not aware of:
Everytime VC is instantiate, viewDidLoad is called once to setup all the important stuff like outlets and such. Then viewWillAppear and viewWillDisappear will be called for in between view swapping. Because it is called just a moment before view is shown to user, all the geometry setting like view orientation and size is set here.
so what I do is:
I addSubview in viewDidLoad, the do all the running setup in viewWillappear and viewWillDisappear.
one more note: view will remain there as long as the app still running.
anyway Thanks rdelmar for helping.
I am trying to get a popup effect and want to design the popup view in another view controller so i can use the xib to do it.
When i used the presentViewController or pushViewController and set the background to transparent, i end up seeing the Window's background color.
I tried this code to add subview to the navigation controller's view so that i can have the Info view cover the entire screen with a transparent background. I also have tab bar to cover up as well.
InfoVC *vc = [[InfoVC alloc] initWithNibName:#"InfoVC" bundle:nil];
[self.navigationController.view addSubview:vc.view];
My problem is inside my InfoVC when i try to dismiss it, the app will crash with some EXC_BAD_ACCESS message:
[self.view removeFromSuperview];
EDIT:
I found a way to stop it crashing but setting the InfoVC as a property in the MainVC. I think the reason for crash is when i call "self.view" in the action inside the InfoVC, it doesn't know that self is the InfoVC inside MainVC.
InfoVC *vc = [[InfoVC alloc] initWithNibName:#"InfoVC" bundle:nil];
[self.navigationController.view addSubview:vc.view];
No no no no. Never never do that.
There is an elaborate dance that you must traverse in order to put a view controller's view inside another view controller's view (or remove it afterwards) if it doesn't come with built-in facilities for doing this (the way a UISplitViewController does, or the way a navigation controller manages the views of the view controllers that are pushed and popped within it).
Read up on customer container controllers. One of the examples from my book is here:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch19p556containerController/p476containerController/ViewController.m
Shouldn't you be using the following to remove the view from its superview?
[vc.view removeFromSuperview];
You can never have a UIView remove it's subviews, the subviews themselves must remove themselves from it's superview. You can easily loop through subviews and have them removed like so
for (UIView *view in vc.view.subviews) {
[view removeFromSuperview];
}
Docs for reference:
http://developer.apple.com/library/ios/#documentation/uikit/reference/uiview_class/uiview/uiview.html
After a "modally" presented view controller has appeared the views under the now presented view controller will be removed; this saves memory, and eases rendering. In your case, though, you also end up seeing the window behind the "modally" presented view.
The natural, and seemingly logical, next step is to simply take one view controller's view and cram it into another. However, as you have discovered, this is problematic. With the newly inserted view safely retained by the view hierarchy it is safe, but the new view controller is not so lucky, it is quickly deallocated. So when this new view tries to contact its controller you will get an EXC_BAD_ACCESS and crash. One workaround, again as you have found, is to simply have the original view controller keep a strong reference to the new view controller. And this can work... badly. There's still a good chance you will get an UIViewControllerHierarchyInconsistencyException.
Of course if you simply want to add a small view you create in IB you don't need to use a view controller as the "File's Owner" and there are many examples of creating an instance of a view from a xib file.
The more interesting question here is, "How would/does apple do it?" Apple consistently says that a view controller is the correct controller for an encapsulated unit of work. For example, their TWTweetComposeViewController, you present it, and it seems to float. How?
The first way of accomplishing this that comes to my mind is to have a clear background that isn't clear. That is, create an image of the screen before the presented view controller appears and set that as the background before the presenting view is removed. So for example(Explanation to follow):
QuickSheetViewController.xib
QuickSheetViewController.h
#import <UIKit/UIKit.h>
#interface QuickSheetViewController : UIViewController
- (IBAction)dismissButtonPressed:(id)sender;
#end
QuickSheetViewController.m
#import "QuickSheetViewController.h"
#import <QuartzCore/QuartzCore.h>
#implementation QuickSheetViewController {
UIImage *_backgroundImage;
}
-(void)renderAndSaveBackgroundImageFromVC:(UIViewController *)vc{
UIGraphicsBeginImageContext(vc.view.bounds.size);
[vc.view.layer renderInContext:UIGraphicsGetCurrentContext()];
_backgroundImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// save an image of the current view, and set our background to clear so we can see the slide-in.
[self renderAndSaveBackgroundImageFromVC:self.presentingViewController];
self.view.backgroundColor = [UIColor clearColor];
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// Time to use our saved background image.
self.view.backgroundColor = [UIColor colorWithPatternImage:_backgroundImage];
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
// Set our background to clear so we can see the slide-out.
self.view.backgroundColor = [UIColor clearColor];
}
- (IBAction)dismissButtonPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
The majority of this example hinges upon the renderAndSaveBackgroundImageFromVC: method. In which, we create a graphics context render the view we are about to cover into it, and then create a UIImage to later (in viewDidAppear) use as a background.
Now simply use it like:
QuickSheetViewController *newVC = [[QuickSheetViewController alloc] initWithNibName:nil bundle:nil];
[self presentViewController:newVC animated:YES completion:nil];
You will see through the background just long enough for the animation to happen, then we use our saved image to hide the removal of the presenting view.
Hy
i have two classes uiviewcontroller and uiview. I have one view controller. Inside i have uiview. Inside uiview i have textfield and when i write a text and click done i need to refresh uiviewcontroller.
I tried with this in uiview class:
-(IBAction)textFieldReturn:(id)sender
{
ViewController *vc = [[ViewController alloc] init];
[vc viewDidLoad];
}
i need refresh the same as you click the button and open viewcontroller.
I am guessing you mean that you want to "refresh" the view, not the view controller. To do that simply call [self setNeedsDisplay] from the view, or [self.view setNeedsDisplay] from the view controller. Also make sure that the textfield is a subview of the uiview. Either do that in the nib file or in code by calling [self addSubview: (textfield here)].
Also, if you want to access the view controller from the view you will need to create an IBOutlet, simply allocating a new ViewController object within the view does not mean that the created view controller controls the view. Hopefully that makes sense. I'd recommend going through some ios starter tutorials as well. Just google that there are a lot.
I'm attempting to embed an editable UITextView inside a UIPopoverController, with... strange results. The steps I've taken are:
Create a custom UIViewController class, and create a .xib file with that controller with a UITextView inside it.
When the UI action that should bring up the controller occurs (touch up inside), I instantiate the controller and its view from the .xib file.
I create a new UIPopoverController, with the view controller I just instantiated as the content view.
I present it with presentPopoverFromRect:inView:permittedArrowDirections:animated:
Here's some example code:
- (void)noteButtonPressed:(id)sender {
self.noteview = [[MyTextPopupViewController alloc] initWithNibName:#"MyTextPopupViewController" bundle:nil ];
UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController:self.noteview];
self.popover = popoverController;
self.popover.delegate = self;
[self.popover presentPopoverFromRect:((UIView*)sender).frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
Then, inside MyTextPopupViewController, I make sure the the text view is the first responder to bring up the keyboard:
- (void)viewDidAppear:(BOOL)animated {
[self.view becomeFirstResponder];
[super viewDidAppear:animated];
}
And that all works... right until it doesn't. Sometimes, it works perfectly; other times, either immediately or after a few keystrokes, the application crashes by exiting the main event loop (!). No exception is thrown (at least not that the lldb catches), but the application simply stops, both on the simulator and on hardware.
Any thoughts? Has anyone gotten this working successfully, or knows for sure that it does not?
I think the UIPopoverController instance needs to be a property in your code.