Draw SubView in Storyboard - iphone

I want to present a modalView (it can be a viewController) that I draw in Storyboard. I don't want to have to make the whole thing programmatically. Is there a way to do this without it being a full screen view?
I guess another way to ask the question is: how do I [self.view addSubView:mySubView] where mySubView is drawn in InterFaceBuilder/Storyboard?

To do this properly, you should look at View Controller containment in the docs. Basically you would addChildViewController after instantiating the viewController from your storyboard and then add the viewController's view to your current view hierarchy.
To just get it working however, the following will get you going:
UIViewController *childViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"<identifier you set in Interface Builder>"];
[self.view addSubView:childViewController.view];
Note that one of the reasons to do it 'properly' will be to ensure that autorotation and presentation callbacks are sent to the sub view controller.

Override the initWithCoder method in the object-c class.
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil] objectAtIndex:0]];
}
return self;
}

Related

UIView loaded with Nib autoresizing issue

I have a UIView subclass -
#interface DatePickerPopup : UIView
UIToolbar *toolbar;
UIDatePicker *datePicker;
#end
#implementation
- (id)initWithFrame:(CGRect)frame
{
NSArray *xib =
[[NSBundle mainBundle]
loadNibNamed:#"DatePickerPopup"
owner:self
options:nil];
self = [xib objectAtIndex:0];
if (self) {
}
return self;
}
#end
and the nib looks like -
In my UIViewController containing the DatePickerPopup (datePopup):
- (void)viewDidLoad
{
datePopup = [[DatePickerPopup alloc] initWithRect:CGRectZero];
CGRect newFrame = datePopup.frame;
newFrame.y = 200.0f; //lets say this aligns it to the bottom in portrait
datePopup.frame = newFrame;
// Normally happens when accessory button pressed but for brevity...
[self.view.superview addSubview:datePopup];
}
- (void)willAnimateRotationToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration
{
CGRect screen = [[UIScreen mainScreen] bounds];
if (toInterfaceOrientation == UIInterfaceOrientationPortrait ||
toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
self.datePopup.frame =
CGRectMake(0.0f, newHeightPortrait, screen.size.width, 260.0f);
}
else
{
self.datePopup.frame =
CGRectMake(0.0f, newHeightLandscape, screen.size.width, 260.0f);
}
}
However, this gets stretched out for some reason when the orientation changes the view gets stretched to the height of the screen bounds - the navigation bar...
after viewDidLoad
after willAutorotate...
Since your view controller appears to be managed by a navigation controller, calling [self.view.superview addSubview:datePopup]; adds your popup as a subview of a UIViewControllerWrapperView, which is one of the private classes UIKit uses to implement the functionality of UINavigationController. Messing with UIKit's private view hierarchy is always risky. In this case, based on the behavior you're seeing, it seems likely that UIKit expects any subview of UIViewControllerWrapperView to be a view controller's view, so it resizes your popup accordingly.
I think the safest way to resolve this is to have your view controller's view be a wrapper that contains your tableView and, when necessary, your popup view. Unfortunately using a wrapper view means that the view controller can't be a UITableViewController. You'll have to change the superclass to UIViewController, set a custom tableView property, and manually adopt the UITableViewDataSource and UITableViewDelegate protocols.
Note: You might be tempted to add your popover as a subview of your window, but I'm not recommending that because UIWindow only autorotates its topmost subview corresponding to a view controller. This means that if you add your popover to your window, it won't autorotate.
EDIT: BTW, by reassigning self = [xib objectAtIndex:0]; in initWithFrame:, you're leaking the object that was originally alloc'd. If you're going to reassign self in this way, you should release the existing object first.
Add the
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
method in the viewController class and return YES. If this method returns YES, only then will the device support landscape orientation. Try out this extra code and see if it helps...
You can set the frame size for landscape in this method itself instead of the current method. PS: I just saw you've used a UIView instead of controller...you might want to change to controller.

place xib in UIView control iphone

I know how to place a nib file in the "main" view controller:
when I execute this method a new nib file will be displayed.
Is it possible to place that nib file in a UIView controller instead?
I want to place that nib file in that view controller instead of in the main view. How could I do that?
Edit:
I tried doing what you mention Paul I don't know what am I doing wrong. Anyways here is what I did:
I created another view controller with the properties an methods you mentioned.
I hooked up my IBoutlet UIView *exampleView to the base view:
then from here I am lost I don't know where to place the other methods. I would like to place that exampleView into:
that view.
I have tried placing them in pgBackground.m but it does not work. I want to place them inside an IBaction so that I can load that with a touch up inside event with a button.
I managed to place a nib file in a uiview control but I have the problem that the subview will not rotate if the device does. I am currently working on a solution to this but so far here is a question that can show you how to add the view from another nib file to a uiview control.
If I follow I think you will need to:
Create a UIView sub class with associated xib
ExampleView.h
ExampleView.m
ExampleView.xib
Set up a property to contain the View hierarchy (everything you want to be hooked up in Interface builder)
ExampleView.h
#property (nonatomic, retain) IBOutlet UIView *exampleView;
ExampleView.m
#synthesize exampleView = _exampleView;
In the ExampleView.m you need to add:
- (void)awakeFromNib
{
[super awakeFromNib];
[[NSBundle mainBundle] loadNibNamed:#"ExampleView" owner:self options:nil];
[self addSubview:self.exampleView];
}
You use awake from nib so you can use this sub class in Interface Builder. If you also want to instantiate it in code you need to hook up the xib in the init method. In this case I would probably extract the nib loading like so:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self loadNibIntoView];
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self loadNibIntoView];
}
- (void)loadNibIntoView
{
[[NSBundle mainBundle] loadNibNamed:#"ExampleView" owner:self options:nil];
[self addSubview:self.exampleView];
}
In the ExampleView.xib file make sure to hook up your IBOutlet UIView *exampleView to the base view.
Now in any xib file where you want to use your custom view simply drag it into your window and change the class to your subclass. This is how I got it working I look forward to some people suggesting improvements.
can u tell me what u want to do? if u want to change orientation then first u declare a bool en type variable in app delegate file then define in app delegate.m file if orientation change then detect and make Boolean type variable true for landscape and false for portrait. this process use for next all views. and u want u execute nib in main view controller then u have two ways one is dynamically and other by interface builder. in interface builder u Select navigation controller and drop in main window file . after that u change the view of navigation controller's view by press cmd+4 and select view which u want to display first.
-(IBAction)btnSubmit_click{
UIDeviceOrientation orientation = [[UIDevice currentDevice]orientation];
if (orientation == UIDeviceOrientationLandscapeLeft) {
// Do stuff
}
else if (orientation == UIDeviceOrientationLandscapeRight) {
// Do stuff like [self setCordinateLandscape];
}
}
-(BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation
{
// Return YES for supported orientations
//set as per requirements ====>>
return (interfaceOrientation == UIDeviceOrientationLandscapeRight);
// Or return TRUE;
}

TouchesBegan on a Sub-ViewController not getting called

I have a ViewController that responds to some touchEvents (touchesBegan, touchesMoved, etc...).
I've found that when I show this controller using presentModalViewController: it works just fine, but I'm trying to add it's View as a subview of another ParentViewController like so:
- (void)viewDidLoad
{
[super viewDidLoad];
//Add SubController
controller = [[SubViewController alloc] initWithNibName:#"SubViewController" bundle:nil];
controller.view.frame = CGRectMake(0, 30, 300, 130);
[view addSubview:controller.view];
[controller release];
}
When I do this, it gets added the parent view but it no longer responds to touch events. Is there a way to fix this?
Also, is there a better way to go about this? I know I probably could have used a View subclass for the child view, but it's supposed to use a Nib and I wasn't sure how to handle that without using a ViewController.
You're correct you should use a UIView subclass.
The easiest way to load it from a nib is to include the subview in your nib.
Just drop a UIView into the view connected to the original view controller.
Then with the view inside selected go to the identity inspector. It's the one that looks like a little ID card.
The very first field is called Custom Class.
Type the name of your UIView subclass here.
If you need a reference to this just create an IBOutlet in your original view controller and hook it up. That way you can set hidden = YES until you need it.
In your UIView subclass you might want to override
- (void)awakeFromNib
This will get called when the nib first unpacks.
for setting up any gesture recognizers, etc.
To load a nib directly into a view :
// Get the views created inside this xib
NSArray *views = [NSBundle mainBundle] loadNibNamed:#"myViewNib" owner:nil];
// There's probably only one in there, lets get it
UIView *myView = [views objectAtIndex:0];
// Do stuff . . .
[[self view] addSubview:myView];
You could try to call becomeFirstResponder in your subview and see whether it receives touchesBegan... It is probably so, but it will also possibly make the superview not receive touchesBegan if you require it...

Common header view on top of nib files with Interface builder

.Hi,
I have a nib file that contains an header that will be used in most of my views, so that I can change it's layout just once when I need. I'd like to know if it's possible to add the header nib view with interface builder, or if I need to do that programmatically and how should it be done.
I've thought about setting the subclass of the subview to a UIView subclass that automatically loads the nib file.
- (id)initWithFrame:(CGRect)frame {
UIView *cell;
NSArray *nib = [[NSBundle mainBundle] loadNibNamed: #"MainHeaderView"
owner: self
options: nil];
for (id oneObject in nib)
if ([oneObject isKindOfClass: [UIView class]])
cell = (UIView *) oneObject;
if ((self = [super initWithFrame: [cell frame]])) {
// Initialization code
}
return self;
}
But this also doesn't seem to work.
It should work, at least in theory. For that matter, it should be possible (but with rather a lot of effort) to add it via IB, from what used to be called a palette, a long time ago; I believe that option was recreated.
I would say that loading from initWithFrame: is likely not to work. Other possible places to load would be awakeFromNib (with a caveat about multiple nib loadings causing it to be called multiple times), or viewDidLoad. Try moving the load to viewDidLoad, and see if your cell is connected up. You should also be testing for failure to load the nib (nil return).
Ok, I've solved this another way.
I've created the headerView and the controller, and a subclass of UIViewController for all views that needed the header to be displayed, loading them all with the header. Something like this:
#implementation MyDefaultViewController
- (void)viewDidLoad {
[super viewDidLoad];
MyTestAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[self.view addSubview: [appDelegate.headerViewController view]];
}
Every view that needs the header to be there will have a controller that's a subclass of MyDefaultViewController. Seems to work, although the fact that I don't specify where to place the header scares me a bit xD

iPhone - UIViewController not rotating when device orientation changes

I have got my own custom UIViewController, which contains a UIScrollView with an UIImageView as it's subview. I would like to make the image to auto rotate when device orientation changes, but it doesn't seem to be working...
In the header file, I've got;
#interface MyViewController : UIViewController <UIScrollViewDelegate> {
IBOutlet UIScrollView *containerView;
UIImageView *imageView;
}
These components are initialised in the loadView function as below;
containerView = [[UIScrollView alloc] initWithFrame:frame];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://..."]];
UIImage *image = [[UIImage alloc] initWithData:data];
imageView = [[UIImageView alloc] initWithImage:image];
[image release];
[containerView addSubview:imageView];
And I have added the following method, assuming that's all I need to make the view auto-rotate...
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
MyViewController loads fine with the image I've specified to grab from the URL, and the shouldAutorotate... function is being called, with the correct UIInterfaceOrientation, when I flip the device too.
However, didRotateFromInterfaceOrientation method do not get called, and the image doesn't seem to rotate itself...
Could someone please point out what I need to add, or what I have done wrong here?
Thanks in advance!
This may not be the right answer for you, because you don't specify the context that the UIViewController's in, but I just found an important gotcha in the Apple documentation that explains the similar problem I'm having.
Tab bar controllers support a portrait
orientation by default and do not
rotate to a landscape orientation
unless all of the root view
controllers support such an
orientation. When a device orientation
change occurs, the tab bar controller
queries its array of view controllers.
If any one of them does not support
the orientation, the tab bar
controller does not change its
orientation.
I've noticed that there are issues when rotating a UIView that's not the first or only view as a direct child of the main window.
So if your UIView is part of a Navigation Controller or a Tab View Controller, you'll also need to override shouldAutoRotateToInterfaceOrientation on the Navigation Controller or Tab View Controller.
Also: using [UIApplication setStatusBarOrientation] helps to work around things if/when you need to do it manually.
To make this kind of thing work in my application, I had to override
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
[self layoutSubviews];
}
and also layoutSubviews
- (void)layoutSubviews
{
NSLog(#"layoutSubviews called");
...recalc rects etc based on the new self.view.bounds...
}
I'm not sure that this is absolutely required, but it worked for me.
Sometimes, if you add a subview to a view, it's your responsibility to make sure that the methods are passed to the subview; a couple of days ago I wrote a short post about this. For example, if you have a UIViewController and add a UINavigationController as subview, you must add this code to the UIViewController if you want viewWillAppear:animated: to be called when UINavigationController.view appears:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[projectNavigationController viewWillAppear:animated];
}
It might be the case that the willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation method also need to be called by the superview; I am not really sure about this, but give it a try.
This is discussed in Apple Technical Q&A QA1688.
Sometimes if you stack multiple views on top of each other for some reason, the anotherController might not receive rotation event.
[myWindow addSubview:primaryViewController.view];
[myWindow addSubview:anotherController.view];
A lazy way (not a good design) to fix this is only add one subview on window, but initialize multiple controller on the app delegate. Then when you need to switch window, remove the current view and add the view you want
[self.view removeFromSuperview];
AppDelegate *dg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[[dg window] addSubview:[[dg viewController] view]];
I just came across this having a similar problem. I have a series of view controllers/complex views, that all rotate perfectly and couldn't figure out why the new one I just added on wasn't rotating. After a LOT of trial and error, the reason was that I wasn't calling the init method (it's the standard init method) when allocating the view controller;
e.g. I was doing
m_timerViewController = [TimerViewController alloc];
instead of
m_timerViewController = [[TimerViewController alloc] init];
To expand on jonoogle's post. I had a similar error. My view has a nib and my custom init:
- (id)initWithCategory:(Category *)category inManagedObjectContext:context{
didn't include the call to init the nib.
self = [super initWithNibName:nil bundle:nil];
Adding that line made my view rotate like it is supposed to.
I copied this from this link
And it works for me.... Reason why i have added this here is to make it easy for others to find. It took me many hours to find this fix:
Make a new set of class files of the UIViewController type, go into the .h file of this class and change this line
#implementation MyTabBarController: UIViewController {}
#end
to something like this
#implementation MyTabBarController: UITabBarController{
}
Now go into the nib file and click on the UITabBarController object and go to it's identity tab, and make it Class MyTabBarController.
now in MyTabBarController.m make sure this is in it.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)io {
return YES;
}
You can probably get rid of everything else in there if you want.
just do this if you what to rotate from landscape to portrait!
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}