96

In complex screens (View Controllers) I used to separate the whole thing in smaller pieces (I call them widgets). These widgets consist basically of a MyWidget.h and a MyWidget.m file as well as a MyWidget.xib file, where the root element is a UIView and the MyWidget class is the File Owner of the UIView. In the init of this widget I do a loadNibNamed.

In my View Controller I then do a [[MyWidget alloc] init], which I add to View's Controller main view as a sub view. This, so far, works perfectly.

I'm now wondering, how to do the same with storyboard, because I cannot really start to drag in a UIView somewhere, I always have to start with an UIViewController, which I don't want to.

If there is no possible way doing this with a Storyboard, can I simply do it the old way, by using the Storyboard for my main screens and segues, and use a separate .xib file to define custom views?

znq
  • 44,613
  • 41
  • 116
  • 144
  • Do you wish to create `xib`s for your view controllers separately and not in the story-board? – tipycalFlow Feb 29 '12 at 11:49
  • @aryaxt: I'm using a mixture of Storyboard and xib's. Storyboard for the main screens and xib's with only an UIView as the root for complex views or widgets. So not really an answer to my original question (by using storyboard), but basically doing the same what I did before already. – znq Apr 02 '12 at 15:54
  • these days, ***just use a container view***, it's really that simple .. http://stackoverflow.com/questions/23399061/objective-c-how-to-add-a-subview-that-has-its-own-uiviewcontroller/23403979#23403979 – Fattie Mar 30 '16 at 12:26

3 Answers3

189

Putting the widget/view in a separate .xib file works, and is appropriate especially if you might want to reference that same view from multiple View Controllers.

However, sometimes you do want to see the additional view/widget within the same storyboard, and it is possible. Here's how you do it:

  1. Select your view controller in IB (click on the black bar below the view), then drag a UIView from the Object Library into the black bar:

    enter image description here

  2. When a view is in the black bar, it's instantiated like any other view in IB but just isn't added to your view hierarchy until you do so in code. Change the view's class to match your own subclass if necessary:

    enter image description here

  3. You can hook it up to your view controller like you would hook up any other view: enter image description here

  4. The added view shows up in your Document Outline and you can hook up actions and references there too:

    enter image description here


Now, the problem that remains is that you can't actually see the view no matter how many times you try to click or double click, which would defeat the whole purpose of putting it in the same storyboard. Fortunately there are two workarounds that I know of.

The first workaround is to drag the view from the black bar back into your view controller's view, edit it, then drag it back into the black bar once you're done. This is troublesome but reliable.

The other workaround is more finicky, but I prefer it because it lets me see all my views at the same time:

  1. Drag a UITableView from the Object Library into your newly added view.

  2. Then drag a UITableViewCell into that UITableView.

    enter image description here

  3. Once you do that, your view pops out magically by the side, but you have a UITableView that you don't want. You can either resize that to 0x0, or you can delete it and your UIView will (usually) still stay visible.

  4. Occasionally the secondary view will become hidden again in IB. You can repeat the above steps if you deleted the UITableView, or if the UITableView is still in the hierarchy you just need to click on the UITableViewCell and the view will appear again.

The second method works for UIViews but not so well for UIToolbars and is impossible for UIButtons, so the cleanest solution I've found when you need to include lots of different subviews is to attach a single secondary UIView to your view controller as a container that never gets shown, put all your secondary views in there, and use the UITableViewCell trick to make everything visible. I resize my dummy UITableView to 0x0 to make that invisible. Here's a screenshot of how it all looks like together:

enter image description here

Community
  • 1
  • 1
nioq
  • 3,215
  • 1
  • 23
  • 18
  • March 2013, I didn't need to do this - you should ignore the black bar completely, and drag/drop your view directly into the main view, and it works fine (Xcode 4.5+) – Adam Mar 10 '13 at 18:40
  • Is it possible to reuse that custom uivew on the same ViewController - If I ex. needed two custom uivews - And if it is how? – Morten Gustafsson Mar 12 '13 at 07:21
  • 2
    Dang, I prefer to just create a separate xib. I probably won't remember all this when I have to update the app in 6 months. – Sandy May 13 '13 at 15:03
  • Though this is an awesome answer, I accidentaly found another better way I think. When hooking up outlets to actual view components. If you hover a little over a component of such an inner view - it pops up for easier selection. Then you see it on the storyboard... – Oded Ben Dov Aug 08 '13 at 14:11
  • Now It is not working in xcode 5, check my question from link: http://stackoverflow.com/q/18931764/952440 – Sunil Zalavadiya Oct 07 '13 at 04:12
  • Sadly they seem to have plugged the bug that allowed this hack to work :( Haven't found any new workaround yet... @OdedBenDov Does your method still work in Xcode 5? I never managed to get that working in Xcode 4 but I was probably doing something wrong. – nioq Oct 07 '13 at 05:15
  • Xcode5 has broken this hack (more like fixed it). Now what to do @nioq – carbonr Oct 07 '13 at 14:01
  • Sorry, haven't tested it yet - I need to upgrade my OSX first. I'll explain the method again, if anyone can test on XCode 5, it'd be awesome: Select an IBOutlet property and drag the connector to your inner view. Hover with the connector (line drawn) above this view, and it should pop up. – Oded Ben Dov Oct 14 '13 at 07:34
  • Make sure that when you add the view (i.e., secondView) from the **Dock** (little black bar) to you view's hierarchy, you also set the frame of that secondView to `self.view.frame`. – KarenAnne Dec 04 '13 at 04:52
  • @OdedBenDov can you explain your method briefly please? I did not get your line for "Hover with the connector (line drawn) above this view".. – Thangavel Dec 20 '13 at 12:26
  • As I wrote above it does not seem to work in XCode 5. But what I meant is that you can drag connectors from outlets to actual components. If you keep holding the mouse button, and bring the cursor above a UIView, and linger there for a bit - the view would open up to let you select components within it. – Oded Ben Dov Dec 23 '13 at 09:37
  • 7
    While this is an incredibly admirable historic answer, really it is now totally out of date. Apple fixed the whole issue by introducing "container views", which is now the central, basic thing you do when making apps - everything a "container view" all the time. Thank goodness, it's now very easy! – Fattie Oct 14 '14 at 08:25
  • @JoeBlow I disagree. This allows a single VC to control logic for subviews. For example, if you have 2 views you want to switch between that both contain a button and a label. Instead of creating two VC subclasses you can just use this approach to centralize the logic and connect all of the buttons into a single VC. Granted, this can be abused but in specific cases can be quite nice. Also, this can help with runtime view decisions, deciding what to present to the user rather than having to programmatically setting up a container and adding a child VC, which is so overkill. – Firo Apr 13 '16 at 15:25
  • hi Firo, hmm, it's hard to see that. thank goodness for container views, it has slashed workload. [tutorial for new devs](http://stackoverflow.com/a/23403979/294884) (the idea of one VC controlling more than one view in a mixed way seems very problematic / complex - I can't see where one would do that.) in your excellent example of similar buttons, of course, just use inheritance in the totally normal OO manner for "similar" views, good idea. for four different views just click four times for four containers. cant see how could be easier. – Fattie Apr 13 '16 at 16:37
11

If you're just looking to make your view controllers else-where(and not in your story-board), then there's a pretty simple way to accomplish this:

1) Create your CustomViewControllers(abcdController in the code I tried) with their individual xibs as usual.

2) Add a UIViewController(or whatever was the superclass of your CustomViewController) to the story-board.

3) Set the CustomClass to CustomViewController instead of UIViewController as shown here:

enter image description here

4) Finally, in your viewDidLoad, load the custom xib and you're done.

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSBundle mainBundle] loadNibNamed:@"abcdController" owner:self options:nil];
    // Do any additional setup after loading the view from its nib.
}
tipycalFlow
  • 7,594
  • 4
  • 34
  • 45
  • 3
    Thanks for your very detailed answer, but I'm actually not looking for a custom view controller, but a custom view (a widget), which gets added to a standard UIViewController. So the view controller is not the problem, but I was wondering what the best approach is to create custom widgets. Either A) do it as I did before, with a .xib file or B) some other approach which I don't know, yet :-) – znq Feb 29 '12 at 14:59
  • 1
    Hmm...I think I got what you want. Basically you want to handle multiple views with the same controller, right? I can't think of a way other than `loadNibNamed` in that case... XO – tipycalFlow Feb 29 '12 at 15:20
  • This solved this problem for me: http://stackoverflow.com/questions/11047991/storyboard-crash-coding-compliant-key-sceneviewcontroller However, instead of viewDidLoad, I used loadView... – Morkrom Oct 29 '13 at 21:36
  • 1
    Just to repeat, this is all (thank goodness!) totally out of date now that container views are here. Just click a container view and it's all done – Fattie Oct 14 '14 at 08:26
1

I think you can do something like this to get instance of specific viewcontroller from Storyboard and use view on top of it.

ex:
MyViewController* myViewController = [[UIStoryboard storyboardWithName:@"Main"  bundle:nil] instantiateViewControllerWithIdentifier:@"myViewController"];
UIView* view = myViewController.view; //Get the view from your StoryBoard.

Hope this helps

Thanks Vijay

vijay_hrd
  • 11
  • 3