How can you create an accessoryInputView in a Storyboard design? - ios

I am trying to create an accessoryInputView to augment the keyboard with some app-specific keys.
I am using a Storyboard based design approach to keep the the UI separated from the application logic.
I cannot seem to understand how to create the new view and associated it with the textView. Is it possible?

You can check this sample project from Apple:
https://developer.apple.com/library/ios/samplecode/KeyboardAccessory/Introduction/Intro.html
To achieve the same result that in the sample, you must drag and drop an UIView instance into the "Document Outline" window when your storyboard is open.
It is the window on the left.
Then you have to create an IBOutlet in your controller to access the view and set it as an inputAccessoryView.

In case anyone else is trying to do this, let me record what I ended up doing.
I created a new XIB with the accessoryView I wanted in it. I could not figure out how to assign
the inputAccessoryView directly into a storyboard design, so I did the following.
I made the fileOwner of the XIB as the controller class that contains the textView that needs the accessoryView and assigned the view to a IBOutlet in that class. That allows me to load the XIB and auto-assign it to the IBOutlet.
Then in the viewDidLoad, I assign the IBOutlet to the textView that needs it. It works well and allows me to do all the UI work in IB and keep the logic in the controller. I guess I can consider setting the inputAccessoryView as 'logic' and not UI design and consider this a good design. Not stellar, since IB does not allow setting the inputAccessoryView directly from a XIB or creating views in a storyboard that are not part of the flow of the app.
The code in viewDidLoad looks like:
- (void) viewDidLoad
{
...
[super viewDidLoad];
[[NSBundle mainBundle] loadNibNamed:#"NoteInputAccessoryView" owner:self options:nil];
_noteTextView.inputAccessoryView=_noteInputAccessoryView;
...
}

Related

Connect UIView in XIB and load it using View Controller in the Story board

I have a XYZViewController (simple UIViewController in storyboard) that is loaded up with the default view. I have a type XYZView for which I have UIView in a .xib file.
In the XYZViewController class, I have defined property for XYZView as an IBOutlet. What is tricky is I don't know how to connect this property to the UIViewController in storyboard (or UIVIew in .xib file) such that —
the IBOutlet is connected to the right UIView
the view in the xib becomes an added subview for the default view of the UIViewController.
(I under the question sounds dodgy and/or I may not have the very right way to explain it, but that's the best I could.)
EDIT: Further clarification may make it easier. I just don't want to myself say:
XYZView *xyzView = [[XYZView alloc] initWithFrame...];
or
[self.view addSubview:xyzView];
Maybe that helps.
OK, from what I tell you have the following...
XYZViewController
The code of this is in XYZViewController.h and .m files.
A .storyboard file
In the storyboard file you have a view controller that you have set the subclass to XYZViewController.
A .xib file
In the xib file you have a single view that you have defined as the subclass XYZView.
Right?
I'm guessing what you have done is the following...
In the .xib file you have laid out the XYZView and put labels, buttons, etc... on it.
The view controller is being created by the storyboard. But now you want to attach the labels and buttons to it.
Right?
If all this is correct then you have a couple of options.
The easiest option
Drop the xib file. Unless that XYZView is being used in multiple places in the app (i.e. inside different view controllers) then you should really be doing all of that layout in the storyboard. Add the buttons and labels to the XYZViewController in the storyboard.
This will then allow you to connect the IBOutlets and IBActions and it will all just work because the storyboard is creating and then setting the outlets and actions.
Next option
Because you have created the view in a xib file you have to load it from that xib file in code and then add it to you view controller.
Something like...
- (void)viewDidLoad
{
[super viewDidLoad];
self.xyzView = [[[NSBundle mainBundle] loadNibNamed:#"XYZView" owner:self options:nil] objectAtIndex:0];
[self.view addSubview:xyzView];
}
Then you can do stuff like ...
self.xyzView.someLabel.text = #"This is the text";
You still won't be able to connect outlets and actions but that's because the view controller is not being created by the xib. It's being created by the storyboard.
This can get all messy though. I'd really only recommend creating a view in a separate xib if it's something that you reuse over and over (like a 5star rating view or something).
What you absolutely can't do
OK, I think I may have thought of what you are doing.
In the storyboard you have set the subclass of the view as XYZView and you are expecting it to pick up the labels and buttons etc... that you have defined in the xib file for XYZView.
This absolutely will not work, ever.
The storyboard and the xib are completely separate objects. If you want to use them together then code is involved in loading a view from a nib and then adding it to a view controller created in a storyboard.

Add custom view in xib file

So I have a custom view created(with a xib file) and I'm trying to use it in another xib file...but it's not showing. In that xib file I have a view and I've set the custom class field to my custom view's name.
However, this view is not showing.
I don't want to draw this view programmatically, I want to use a xib file - how do I do this?
I'm not sure what code I can post up - just tell me in the comments.
I'm not aware of a method to directly drawing a custom view with a xib to another xib, but a thing you can do is:
assing the file owner to the view controller of the xib you want to put the custom one in.
assign the view as an IBOutlet of that controller.
when you need the outlet simply call: [[NSBundle mainBundle] loadNibNamed:#"your_custom_view_xib_name" owner:self options:nil];, at this point the outlet previously created will be set.
set the outlet as subview of the view

Is there a way to 'design' a custom UIView in storyboard, and use in multiple ViewControllers without adding to any specific controller?

I'm in need of a custom UIView which is to be used in multiple different ViewControllers.
Usually when I create a custom UIView, I drag an instance of UIView into my ViewController in storyboard, create a subclass of UIView, and point to this subclass in the Identity Inspector for the view. From there, I would connect the UI-objects as outlets to the header-file of the subclass.
The view I want to make now, is not supposed to be a part of any specific controller, but should be added to any controller that asks for it. A rogue UIView. I know I can create the entire UIView programmatically, and just create an instance of it, but I'd like to have it (and design it) in my storyboard.
In Storyboard, the only objects I'm allowed to drag 'outside a ViewController', are other ViewControllers.
I have never used anything other than Storyboard for iOS-developing, but I have come over tutorials which have been using the other mode, from the olden days, which looks like what I need. Is it possible to get something similar into my storyboard? Or would this require its own setup/design? If so, how?
I was also thinking of solving this with adding a 'phantom' UIViewController containing my custom View; designing it there, but instantiate it from the other controllers, but this sounds so hacky..
I'd like to do this with a UITableViewCell as well, but I guess that would have the same solution.
For your UIView, you should be creating a custom UIViewController in your storyboard and instantiate the view controller to access the view:
MyViewController *myViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MyViewController"];
// Access the view using "myViewController.view"
This is a very common practice in iOS since storyboards were presented. If you are using multiple storyboards, you should create a new instance of UIStoryBoard with:
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:#"StoryboardName" bundle:nil];
And then instantiate the view controller with this instance of the storyboard.
If you want to solely use a UIView, you should be creating a .xib file, i.e. the olden days format. To create a custom UITableViewCell, I would absolutely use a .xib file. Your last option would be to create a UIView programmatically, which could be called from any place in your application. Depending on the complexity of this view, this may be a valid option.
I don't think you can create a custom UIView in storyboard. So in this case, you can create a xib file for that custom view. Then when you want use that view, just use
UIView *customView = [[[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil] objectAtIndex:0];
to get that view.

Custom views with Storyboard

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?
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:
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:
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:
You can hook it up to your view controller like you would hook up any other view:
The added view shows up in your Document Outline and you can hook up actions and references there too:
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:
Drag a UITableView from the Object Library into your newly added view.
Then drag a UITableViewCell into that UITableView.
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.
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:
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:
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.
}
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

Does XIB always need a view controller?

If I do an interface in IB must I always need a base UIViewController or can I just skip straight to a UIView ?
As of now I'm doing all my design in obj-c which makes for a bit more busy work.
If I have to use a UIViewController is there anyway to suck the UIView out of it if that's all I want?
I just want to be able to pull out static layouts from XIB instead of putting them together in obj-c.
Any suggestions are appreciated!
UIViewController has that handy initWithNib: bundle: method that makes everything so easy, but there are also ways to get objects from xibs via:
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"WhateverItsNamed" owner:self options:nil];
(and the nib array here contains objects which correspond to whatever objects are stored in the XIB file).
No, you don't need a UIViewController if it doesn't serve your needs.
After you have initialized a UINib, use its:
-instantiateWithOwner:options:
to access the nib contents. Make the nib's root object be a UIView (or subclass thereof). If you plan on making connections to File's Owner you will need to set File's Owner to a context-appropriate class.
No, you don't need to associate a UIViewController with a XIB.
First, do not add a view controller to the XIB. Then, you'd often use NSNib or UINib APIs to access the top level objects of the nib -- so you can avoid a view controller and set up any structure you'd like in the NIB editor, and programmatically access the objects in the NIB as needed.
In iOS 5, XIB are not recommended to be used anymore, you should use a storyboard instead.
And to simply answer your question, every top level view requires a view controller to be responsible for it.
All sub views can be an outlet in your view controller and be handled from there.

Resources