Quick question. Is this possible to create storyboard to landscape orientation? I have to change some positions of elements in views and I can't use auto-layout to deal with it. Of course I can write it in code, but I think it's unnecessary. I'm quite new in iOS developer, but I was working as Android developer. There we could create different folders for every instance. I think it should look like this:
Main.storyboard
Main_iPad.storyboard
Main_landscape.storyboard
Main_landscape_iPad.storyboard
Of course creating different files is not a problem. Problem is: how to link it properly in my code/plist/etc.
Warning: Don't create a separate nib/storyboard for landscape version of the same view unless your layout is completely different. Auto-Layout should handle layout changes upon orientation change.
Assuming you have good reasons to do this:
You can have different nibs/storyboards for the same view/view controller. Just implement each as if you have only one nib/storyboard. Then create the view/view controller as follows:
For nib:
NSString *nibName = #"NAME OF ONE OF THE NIBS";
NSArray *nibs = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
id view = [nibs firstObject]; // Ideally, iterate through the array and check class, then return the view.
For storyboard:
NSString *storyboardName = #"NAME OF ONE OF THE STORYBOARDS";
NSString *viewControllerIdentifier = #"VIEW CONTROLLER IDENTIFIER";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:viewControllerIdentifier];
Related
I try to create something like SDK - pack of default views, controllers wich developer can extends and customise
I have a problem with understanding how user can extends my UIViewController if his views didn't make programmatically, but created in separate .storyboard.
For UIView, we do something like this
-(id)init{
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"myXib" owner:self options:nil];
return [subviewArray objectAtIndex:0];
}
How UIViewController can get own view from .storyboard?
I can't use .xib files becose my viewControllers have TableViews with dynamically TableViewCells.
UPD:
I think it easy create UIViewController programmatically , add coupe views and all ChildViewControllers will have the same controls as his ParentViewController.
I want to add not programmatically this controls, but use .storyboard for this. And I can't imagine - how to bound this.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle:nil];
MyViewController * myVC = (MyViewController *)[storyboard instantiateViewControllerWithIdentifier:#"MyViewControllerIdentifier"];
The one who is using your library, can simply set the class of his ViewController to your ViewController from storyboard like :
I have my normal view controller, but once in awhile I'll present something of an options screen atop the view controller. I don't know how to set this up in a Storyboard though, as if I had it on top and then set hidden to true, it would obstruct all the other views and be rather annoying to fiddle around with.
What should I be doing in this case?
You can create the view in a separate XIB file, and then load the view from your view controller programmatically. If this view is showed often you can keep the view in a property, otherwise you can just load it from the XIB every time. To load a view contained in a XIB:
UIView *view = [[[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil] firstObject];
[self.view addSubview:rootView];
If this view is something like a settings, I'd recommend to use a separate view controller. You could use a Storyboard and then programmatically add it as a child view controller.
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle:nil];
UIViewController *vc = [sb instantiateInitialViewController];
[self addChildViewController:vc];
content.view.frame = self.view; // here you set the frame of your view
content.view.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
[self.view addSubview:vc.view];
[vc didMoveToParentViewController:self];
Are you trying to create a view that is smaller than the view controller?
If that's the case then you can create one programmatically by sub classing UIView.
But if it will cover your entire view controller then you can create a view controller with a .xib file. You can then draw everything on it using IB and then call that view controller morally.
I have tried pushing to my ChatView view controller from my UserProfile view controller in a number of different ways:
UserProfile.m
ChatView *chatView = [[ChatView alloc] init];
[self.navigationController pushViewController:chatView animated:YES];
Also:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
ChatView *chatView = [storyboard instantiateViewControllerWithIdentifier:#"chat_view"];
[self.navigationController pushViewController:chatView animated:YES];
And also I have tried modal presentations:
[self presentViewController:chatView animated:YES completion:nil];
These don't actually crash, but they all fail to get past this bit of viewDidLoad:
JSQMessagesViewController.m
-(void)viewDidLoad
{
...
NSLog(#"We see this log");
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([JSQMessagesViewController class])
owner:self
options:nil];
NSLog(#"We do not see this log");
...
}
Additional info
The above code all works absolutely fine when I push to this view from my RecentChats view controller. The key difference, I think, is that my UserProfile view controller itself loads nibs, whereas RecentChats is a more straightforward Storyboard-built UITableViewController, and I wonder if is the nib loading in UserProfile this is causing issues.
The short answer is you are doing many fundamental things wrong and they are culminating in your code not working.
You should read Apple's View Controller Fundamentals.
To name a few problems, you are using the wrong initialize function for a View Controller. Unless a subclass designates otherwise, -initWithNibNamed:bundle:(typing from memory on my phone; sorry if I'm not exact) is the correct method.
Your view controller's main view is loaded in the -loadView method, not -viewDidLoad. If you are using a NIB, the name of the NIB should be passed into the initialize method, not loaded via -loadNibNamed:.
I could go on for a while, but you get the point.
I'm completely re-formulating this question having learned that I was originally off track but that having me no closer to solving the problem. With reference to this image...
I am wanting to either create or manipulate the segue (highlighted in yellow) in code such that the Master view is any one of a number of subclasses of MFMasterViewController (highlighted in red).
When doing this using Nibs I could create a Nib, SharedNib.xib & set the class as MFMasterViewController, then create my subclasses, say MFMasterViewControllerSubclassA, MFMasterViewControllerSubclassB etc. & then instantiate whichever subclass I wanted using...
MFMasterViewControllerSubclassA *controller = [[MFMasterViewControllerSubclassA alloc] initWithNibName:#"SharedNib" bundle:nil];
or...
MFMasterViewControllerSubclassB *controller = [[MFMasterViewControllerSubclassB alloc] initWithNibName:#"SharedNib" bundle:nil];
etc.
Any clues as to how I can get this right using storyboards?
In my case the reason for wanting to do this is that all my subclasses are the same tableview & data but sorted differently & having some difference in what's written to the detail text of the cels. I suspect that it is a not uncommon pattern.
Cheers & TIA,
Pedro :)
It's not a direct answer but this is how I would accomplish what you want based on your explanation of the reason.
Basically you need to separate the UITableViewDataSource (and maybe the delegate too) from the MFMasterViewController so when the segue is executed you can set the correct dataSource and delegate in the view controller.
So in the Navigation Controller you need to implement the prepareForSegue:sender: method. This is where you can customize the segue before it is executed:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// you can set the segue identifier using Interface Builder
// also it is a good thing to make sure which segue you're using
if (([segue identifier] isEqualToString:#"TheId"]) {
id<UITableViewDataSource> dataSource = [[TableViewDataSourceImplementationA alloc] init];
[[[segue destinationViewController] tableView] setDataSource:dataSource];
}
}
This way you can get the customization you want without the need to create subclasses of your view controller.
And if you have access to WWDC videos, check the session #407 Adopting Storyboards in Your App.
For anyone stumbling upon this question, you should also consider more generally using a "Strategy" pattern as an alternative to subclassing your controller. The accepted answer is a form of that, where the strategy implemented comes from whatever the data source/delegate is, and can be switched out at runtime. Another example of this is https://stackoverflow.com/a/17381927/954643
If your .m file is not associated with any storyboard, wouldn't self.storyboard be Nil?
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:
#"MainStoryboard" bundle:[NSBundle mainBundle]];
ViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"HauptMenu"];
Make sure to change the storyboardWithName: to whatever your storyboard is named.
NSString * storyBoardName;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
storyBoardName = #"MainStoryboard_iPad";
} else {
storyBoardName = #"MainStoryboard_iPhone";
}
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:
storyBoardName bundle:[NSBundle mainBundle]];
ViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"HauptMenu"];
I believe i finally found the answer. We want to use storyboard ViewController with another class name. There are many workarounds like using delegates but i think this is the best one. I already answered it in another topic. Hope it helps!
https://stackoverflow.com/a/32103618/1943053
i'd like to handle orientation change on an iPad application with one UIViewController and two XIBs, let's say MenuView and MenuViewLandscape.
So, in the willRotateToInterfaceOrientation method of the MenuViewController, how can i change XIB without using another controller for the landscape mode ?
I'm using the following code:
if( toInterfaceOrientation != UIInterfaceOrientationPortrait ){
MenuViewController *landscape = [[MenuViewController alloc]
initWithNibName: #"MenuViewLandscape"
bundle:nil
];
[self setView:landscape.view];
}
else {
MenuViewController *potrait = [[MenuViewController alloc]
initWithNibName: #"MenuView"
bundle:nil
];
[self setView:potrait.view];
}
But when i go to landscape view the XIB the landscape view controls are not properly rotated.
I'm not sure there are any strange side-effects with this implementation, but try something like this and see if it works for you:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
if (UIInterfaceOrientationIsPortrait(orientation)) {
[[NSBundle mainBundle] loadNibNamed:#"MenuView" owner:self options:nil];
if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
self.view.transform = CGAffineTransformMakeRotation(M_PI);
}
} else if (UIInterfaceOrientationIsLandscape(orientation)){
[[NSBundle mainBundle] loadNibNamed:#"MenuViewLandscape" owner:self options:nil];
if (orientation == UIInterfaceOrientationLandscapeLeft) {
self.view.transform = CGAffineTransformMakeRotation(M_PI + M_PI_2);
} else {
self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
}
}
}
This assumes that the File's Owner in your MenuView and MenuViewLandscape XIBs are both set to MenuViewController and that the view outlet is set in both XIBs as well. All of your outlets should be reconnected properly on rotation when using loadNibNamed.
If you are building for iOS 4, you could also replace the loadNibNamed lines with these:
UINib *nib = [UINib nibWithNibName:#"MenuView" bundle:nil];
UIView *portraitView = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
self.view = portraitView;
and
UINib *nib = [UINib nibWithNibName:#"MenuViewLandscape" bundle:nil];
UIView *landscapeView = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
self.view = landscapeView;
These assume that the UIView that you want to display immediately follows the File's Owner and First Responder proxy objects in the XIBs.
Then you just need to make sure the views are rotated properly for the interface orientation. For all of the views that are not in the default portrait orientation, rotate them by setting the transform property of the view and using CGAffineTransformMakeRotation() with the appropriate values as shown in the example above.
The rotation alone might solve your issue without the extra loading of the NIBs. However, loading a whole new instance of a MenuViewController and setting its view to the existing MenuViewController's view might cause some strange behavior with lifecycle and rotation events, so you might be safer trying the examples above. They also save you the trouble of having to create new MenuViewController instances when you only need the view from it.
Hope this helps!
Justin
Perhaps the answer from Jon Rodriguez here will do what you want:
Want to use muliple nibs for different iphone interface orientations
If you have two UIViewController classes, a base class for portrait mode and a subclass of that for landscape mode, you can put almost all the code in the base class. So that gives you most of the advantages of a single view controller class while also allowing you to use other solutions like this:
Easiest way to support multiple orientations? How do I load a custom NIB when the application is in Landscape?