I am developing an application that currently has a View Controller (call it ViewControllerX). The MainWindow.xib file contains the following:
File's Owner UIApplication
First Responder UIResponder
AppX App Delegate myAppDelegate
myViewControllerX myViewControllerX
Window UIWindow
When I look at the MainWindow.xib in Interface Builder it shows View Loaded from "myViewControllerX". The myViewControllerX.xib file for this contains the following:
File's Owner UIApplication
First Responder UIResponder
MainView UIView
Image View (image1.jpg) UIImage View (Inside MainView)
Rounded Rect Button UIButton (Inside MainView)
Rounded Rect Button UIButton (Inside MainView)
Table View UITableView (Inside MainView)
On the Table View, I have the Outlets set to dataSource = File's Owner and Delegate = File's Owner. The Referencing Outlet is set to mTableView which is defined using IBOutlet in XCode. When I run this all works fine and the table gets populated.
When the users clicks an Item within the Table View I want a new view to slide into place, which also contains a TableView (basically the first View Controller shows an overview list, when the user clicks on an Item I want it to show the details of the choice). In the myViewControllerX.m file, I have the following code:
- (void)tableView:(UITableView*)theTableView didSelectRowAtIndexPath:(NSIndexPath*)theIndexPath
{
[self.navigationController pushViewController:self.mViewControllerY animated:YES];
}
I have created a ViewControllerY and have the following files:
ViewControllerY.xib, ViewControllerY.m and ViewControllerY.h.
In the .xib file for ViewControllerY, I have:
File's Owner UIApplication
First Responder UIResponder
Table View UITableView
When I run this it works (so the code and the hook-ups in Interface Builder are ok). The Problem is it takes over the whole of the view whereas want I want it to have an Image (and other UI objects) as well as a Table View. I've tried changing the UITableView of ViewControllerY to be of type UIView and then adding an UIImageView and UITableView inside of the UIView in a similar way to ViewControllerX but I can't get it to work and now I'm not sure what to do! So, my question is, how do I go about Implementing this? I'd like to be able to have it setup in Interface Builder, so how to I change it to handle this? e.g How do I hook up the dataSource, Delegate and Outlets etc.
Thanks in Advance for any help in this. I've tried all kinds of things but I just can't seem to get it to work. I'm sure I'm almost there and must be missing something that is very obvious!
All the Best
Dave
Your UINavigationController uses a content view that more or less completely covers your device screen. And this view is used for your main view. If you ask this UINavigationController to push another view, he uses this very same view to swap in the ViewControllerYs view which also covers the complete screen.
To gain the behavior you need, you should create a second "embedded" Navigation Controller instance which is responsible only for that part that is initially by your table view. Display your table view as the main view and then ask this Navigation Controller to swap in your other views into this part of the screen.
After taking a step back and doing a bit more digging, I came up with the answer.
High Level Explanation.
ViewControllerY needs to be of class UIViewController, not UITableViewController. The ViewControllerY : UIViewController definition in the .h file should include the Table View Delegate and the Data Source protocols. A member of the ViewControllerY has to hold the UITableView* - call it mTableView and should be defined as an IBOutlet.
The table view delegate methods should then use self.mTableView to access the table.
In Interface Builder, the UIView Object needs to have an Outlet "view" hooked up to the File's Owner and the Table View inside the UIView needs to have the dataSounce and the Delegate set to File's Owner and the mTableView Outlet also needs to be hooked to the File's Owner.
Low Level Explanation.
in ViewControllerY.h do the following:
#interface ViewControllerY : UIViewController <UITableViewDelegate,UITableViewDataSource>
{
UITableView* mTableView;
}
#property (nonatomic, retain) IBOutlet UITableView* mTableView;
In ViewControllerY.m, in the Table View Delegate methods, access the mTableView like so:
myNewCell = (UITableViewCell*)[self.mTableView dequeueReusableCellWithIdentifier:myCellIdentifier];
(obviously you need to define and handle all the other Table View Methods in the normal way.
ViewControllerY.xib should have the following in it:
File's Owner ViewControllerY
First Responder UIResponder
View UIView
Image View UIImageView
Table View UITableView
Control Drag from the File's Owner to the View and select "view".
Control Drag from the Table View to the File's Owner and select Data Source.
Control Drag from the Table View to the File's Owner and select Deligate.
Control Drag from the File's Owner to the Table View and select mTableView.
That's it! You can then add other UI objects inside the UIView and hook them up as appropriate in the normal way. I'm not sure if this is the only or best solution but is works and I'm happy with it.
Hope this helps someone with the same or similar problems. It's a bit confusing til you get the hang of it.
Related
Recently, I've been trying to learn Swift, so if this seems to be a relatively simple question do forgive me.
For some reason, control-clicking a NSTextField object in a tab view controller and dragging doesn't give me the option to "insert outlet or action" but rather, "connect binding" when I scroll over a compatible object (in this case it was the superclass declaration).
Why is it that I am not able to insert an outlet or an action, but am able to connect a binding?
side question: what is a binding?
The outlet can only be a property of your nib file / storyboard scene's owner. Which is your view controller in most cases.
This is what happen what your view controller instantiated from storyboard:
Instantiates views using the information in your storyboard file.
Connects all outlets and actions.
Assigns the root view to the view controller’s view property.
Calls the view controller’s awakeFromNib method.
When this method is called, the view controller’s trait collection is empty and views may not be in their final positions.
Calls the view controller’s viewDidLoad method.
Use this method to add or remove views, modify layout constraints, and load data for your views.
Short Version:
UIKit instantiate your view controller for you, and add all subview as you required in storyboard. And connect(binding) subViews to their outlet (if you created one).
How to solve your problem
When you opened your assistant editor. Choose the automatic viewController, if that is not something your create, you should create a subclass of viewController you need (in this case, UITabViewController) and change your scene's class to that.
you need to have same view controller in storyboard when you control-drag a NSTextField to controller
Edit: As Leo mentioned in the comment You can't connect/create any IBOutlet in the AppDelegate file if you are using storyboard, you have to create in it's particular controller where you have placed NSTextField
Storyboard
As shown in the image if you are in storyboard click the top bar on the controller from there you will get the options shown in the image, select Automatic
If you have selected proper view controller in the storyboard it should show same viewcontroller file in the Automatic(viewController.swift) by selecting that file you should able to control-drag IBOutlet
XIB
In the Xib when you select Xib and click 'Asssistant Editor' it will generally take to the proper view controller if not select Automatic for it also as shown in the Storyboard image are that you will able to connect you IBOultet
Binding
When you control-drag IBOutlet it create a connection between your control and property of view controller
From Apple Doc
The Cocoa bindings feature enables you to establish a live connection between an item of data and its presentation in a view. Any change made to the data’s value, either in the view or in the object that stores the data, automatically propagates across the connection.
You can find more information regarding it in Apple Doc
I have an WizardSequenceViewController with an IBOutlet WizardView *_wizardView. In many WizardSequenceViewController.xib file I have the view outlet connected to the File's Owner - WizardSequenceViewController. I have a subview of that view defined with the class attribute set to WizardView. I have connected that WizardView to the outlet in the File's Owner. Finally, in my WizardView.xib I have a UILabel that I have placed in the file to test if the view is being rendered. When I select the WizardSequenceViewController from my tab bar, I see the superview view but not the subview _wizardView. When I set a breakpoint in my -(id)initWithCoder method in my WizardView.m file I see it stop there, so I know that it is calling that initializer (and thus it should be using the xib to load that file). I have tried many iterations and variations to get this thing to work but I can't and I am going crazy. Does anybody have any ideas?
From Apple doc "View Controller Basics, About Custom View Controllers":
The one-to-one correspondence between a view controller and the views in its view hierarchy is the key design consideration. You should not use multiple custom view controllers to manage different portions of the same view hierarchy. Similarly, you should not use a single custom view controller object to manage multiple screens worth of content.
Note: If you want to divide a view hierarchy into multiple subareas and manage each one separately, use generic controller objects (custom objects descending from NSObject) instead of view controller objects to manage each subarea. Then use a single view controller object to manage the generic controller objects.
Maybe you can't do a view-and-subview outlet setup in a view controller. And I'm not sure assigning the subview outlet to a separate NSObject subclass would work either, because how would you present it? Could you write your subview programmatically, using initWithFrame and addSubview, instead of making it an outlet? Or, if you really want to set it up graphically, could you assign it to a separate view controller as owner? Then the top view controller will call presentModal on the sub view controller. Or, if all you need is a UILabel as a subview, just add the label to the main view?
Even I faced a similar issue. But got it resolved by following steps given in the following link. Hope it helps.
http://blog.yangmeyer.de/blog/2012/07/09/an-update-on-nested-nib-loading
I have a tab bar application. In one of my tabs, there is a search bar and a table view below that. When you enter something into the search bar, it returns parsed xml. I need to put this parsed information into the tableview below. The class inherits from UIViewController. I declared a UITableView object in the header file and linked it in interface builder, and adopted the UITableViewDelegate protocol.
I'm not sure If i'm going about this the correct way. Any help?
That sounds about right. You will need to also have your UIViewController implement UITableViewDataSource, and add the methods from that protocol to populate the table.
There are various tutorials available which guide you step-by-step to create a simple tablview. Google them. You can follow these steps for connecting your tableView outlets:
Select your viewController where you are displaying the tableview to be your "File's Owner" in Identity Inspector.
Drag your view's outlet to your File's owner.
For tableView inside view, drag its outlets to File's owner again so that your datasource and delegates are up. And in the same view, drag your referencing outlet to the IBOutlet you have created in your viewController class.
If I have a UIScrollView set up in the view via the Interface Builder, how do I get a reference to it in the ViewController implementation? I want to programmatically add labels to the scroll view.
For example, in C# if you have a textbox declared in the UI/form, you can access it by simply using the ID declared for that textbox. It doesn't seem this simple in objective c.
Thanks
Kevin
Assuming I understand you rightly and you are instantiating a view controller from a .xib containing subviews including the UIScrollView you want, there are two ways - first, you can find it in the subviews array that is owned by the view controller. Second, you can add an IBOutlet reference to it in your header file, then in interface builder make the connection using the connections inspector. Then you can refer to the object in your code, change frame, add labels etc.
You need to wire your ViewController up to your Nib files. This is pretty straightforward. This is your basic workflow for using Interface Builder on the iPhone/iPad:
Set the Class of the 'File's Owner' to the class of your view controller. You can do this by selecting the 'File's Owner' object in your nib window, pressing Command-4, and setting the class via the drop-down.
Create properties in your View Controller with the following format:
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
The IBOutlet keyword is a macro that evaluates to nothing. So it doesn't actually do anything to your code, it just exists to let Interface Builder know that the 'scrollView' property can be bound to.
Control-drag from the object you'd like to bind to your ViewController. In the popup you can select the property you'd like to bind to the scroll view object.
This sort of stuff is pretty basic Xcode stuff. If you read any tutorial out there it'll cover this. Good luck, and enjoy!
edit:
I should add that if you used the default "New UIViewController Subclass" from the New File dialog, it will have done step one for you. You'll have a nib file and a View Controller that already know about each other.
Yes, the code you wrote is all you need in your header. Just make sure you connect the Scroll View object to the property in Interface Builder.
Yup! 'viewDidLoad' is added after all the connections specified in the Nib file have been made, so you can be confident that scrollView point to the correct object (assuming everything in the Nib is wired correctly, which is an easy mistake to make)
I'm having trouble creating a UIView (in Interface Builder) that contains a UITableView with some other object, such as a UIButton or UILabel. The UITableView always takes up the maximum amount of space in the UIView, regardless of the size of the UITableView object itself. The File Owner is a standard UITableViewController.
Here's how to do this easily:
1) Create a table view controller with xib.
2) Change the inherited type in the .h file from UITableViewController, to UIViewController.
3) Add the protocols UITableViewDataSource, UITableViewDelegate to the .h file.
4) Open the xib, add in a view.
5) drag the table view in the xib under the view, resize as desired.
6) Wire the "view" property of the File's Owner to the View instead of the UITableView. The datasource and delegate properties of the table view should still be wired to File's Owner.
7) (optional) if you want to be able to reload or otherwise access the table outside of table view controller delegate methods that pass in a table view, make a UITableView * IBOutlet named "myTable" or the like, and wire the table in IB to that.
An alternate approach is to make a new UIViewController with xib, add a table to the xib, wire datasource/delegate to the file's owner, and make a new UITableViewController class which you use to copy the methods from into your view controller, then delete.
Unfortunately, creating a UITableView in Interface Builder (IB) is was very problematic for me. I ran into the same problems as you as a beginning developer and, after much frustration, just ended up abandoning IB for UITableViews.
The best solution for me was to just implement the UITableViewController (and the UINavigationController that you use as a header) programmatically. Once you figure out the whole Model-View-Controller paradigm, it is actually fairly straightforward.
Some good resources for dealing with them programmatically can be found in Apple's documentation with these names:
"Table View Programming Guide for iPhone OS"
"View Controller Programming Guide for iPhone OS"
Cocoa with love has an article about Recreating UITableViewController to increase code reuse. This is useful if you can't use a UITableViewController, but want to make sure that your UIViewController will behave the same way.