Preferred Idiom for UIViewController XIBs - iphone

In most sample and generated examples, the structure of XIBs for UIViewController subclasses is:
Placeholders
File's Owner = Class of UIViewController subclass
First Responder
Objects
View (wired to view outlet of owner)
However this structure does not allow for previewing of layout: File's Owner does not support the attributes inspector to display "simulated metrics". In order use the attributes inspector for the view controller, the following XIB structure is necessary:
Placeholders
File's Owner = Class of UIViewController subclass
First Responder
Objects
A UIViewController
View (wired to view outlet of owner)
My question is: is there any reason not to do this? The containing UIViewController seems to be ignored when the XIB is loaded, so it seems to just do the job of a container for purposes of prototyping and previewing in the IB.

However this structure does not allow for previewing of layout: File's Owner does not support the attributes inspector to display "simulated metrics".
Any root UIView instance does.

Related

Connecting an NSTextField object to an IBOutlet in AppDelegate.swift

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

How can I populate UITableView with XML?

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.

UITableView and UIImage inside a UIView

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.

UITableView uses all available space in Interface Builder

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.

Creating a nib file for a Table View Controller

I was trying to follow the Table View Programming Guide for iPhone OS but was having trouble creating a new Table View Controller that loads its data from a nib file.
Specifically, I was trying to follow the steps in this part:
If you prefer to load the table view
managed by a custom table-view
controller from a nib file, you must
do the following:
In Interface Builder, create an empty Cocoa Touch nib file (File >
New).
Drag a UITableViewController object from the Interface Builder
Library into the nib document window.
Save the nib file in your project directory under an appropriate name
and, when prompted, select your
project to have the nib file added to
it.
Select Table View Controller in the nib document window and open the
Identity pane of the inspector. Set
the class to your custom table-view
controller class.
Select File’s Owner in the nib document window and set its class
identity to the custom table-view
controller class.
Customize the table view in Interface Builder.
Select the table-view controller in the nib document window, open the
Attributes pane of the inspector, and
enter (or select) the name of the nib
file in the Nib Name field.
So I created a new UITableViewController subclass in Xcode (called "MyTableViewController"), then I went into IB and followed those steps. I made sure to link up all the Class attributes to the same name as the UITableViewController subclass I made in Xcode like it says in the steps.
But now I get the following warning in IB:
"My Table View Controller" has both its
"View" and "Nib Name" properties set.
This configuration is not supported.
When I run the application and push the table view controller, it appears but it seems like nothing is being loaded from the nib file at all (e.g. I set the alpha to 0 instead of 1).
Any idea as to what I'm doing wrong?
Thanks for the help.
Here's some more information that might help you understand the situation better.
I noticed a few differences between creating a UITableViewController with the template (e.g. by creating a new Navigation-based Application) vs. creating one yourself (e.g. following the steps above). I'm going to refer to each as TemplateNib and CustomNib, respectively, to make it easier to understand the differences.
In TemplateNib, it has the following objects in the document window:
File's Owner
First Responder
Table View
In CustomNib, it has the following objects in the document window:
File's Owner
First Responder
My Custom Table View Controller
Table View
Another difference is in the File's Owner links...
TemplateNib's File's Owner:
Outlets
tableView -> Table View
view -> Table View
Referencing Outlets
dataSource -> Table View
delegate -> Table View
CustomNib File's Owner:
Outlets
view -> (nothing)
CustomNib My Table View Controller:
Outlets
view -> Table View (this is grayed out so you can't delete it)
Referencing Outlets
dataSource -> Table View
delegate -> Table View
Update:
I tried to mimic the .xib file that is created by the template by following these steps:
Created an empty file in Interface Builder.
Set the File's Owner to the class that inherits from UITableViewController.
Added a Table View to the document window.
Set the Table View's dataSource and delegate to File's Owner.
Set the File's Owner view to the Table View.
Added a tableView propery in the Identity pane of type UITableView.
Set the File's Owner tableView property (which I just created) to the Table View.
However, this still seems like it is not loading it from the NIB file. (I also never set the name of the NIB file anywhere though... is there anyplace I need to set it or does it look for one with the same name?).
I then tried overriding initWithNibName to load from the name of the nib file, and now it does seem to load it from the nib file. However, if I look at the .m file of the TemplateNib table view controller, it doesn't need to override this method, why is that? I still think I am doing it the wrong way cause the Programming Guide didn't mention anything about doing it this way.
Update:
I tried comparing the two .xib files using a diff tool, the only significant difference between the two seems to be:
<string key="superclassName">UITableViewController</string>
// and:
<reference key="NSSuperview"/>
I don't see any reference to the Nib file in the original file anywhere, are there any other files I should check?
Update:
It seems like the thing that makes TemplateNib load from the nib file is that in the MainWindow.xib (default name given by the template), the RootViewController is added with the NIB Name property having the value "RootViewController". Additionally, its class is set to "RootViewController".
I tried putting a breakpoint in both initWithNibName:bundle: and initWithStyle: on the RootViewController, however, it never gets to there. I'm kinda wondering how the TableViewController is created when you set it up in the MainWindow.xib like that.
I also tried adding the my custom table view controller to MainWindow.xib, setting the class and nib names in hopes that it will load it from the nib file I specified, but it doesn't even call iniWithNibName.
Create a TableViewController in Xcode.
Create an empty nib file in Interface Builder.
Set the File's Owner Class property to the TableViewController from step 1.
Add a TableView to the empty nib file.
Set the File's Owner view property to the TableView from step 4.
Customize the TableView in IB as you want.
Override the initWithNibName:bundle: method in Xcode for the TableViewController you created and use code similar to the following:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:#"MyNibName" bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
I had this same problem last night and found this post while trying to search for the answer. I ended up solving it.
Basically I had opened up the wrong XIB file (I hoped main_window.xib, not the view controllers xib)
I cut all the controls from my main xib, pasted them into the controllers xib, realigned everything, reconnected all the outlets/actions and the warning went away :)
Hope this helps someone :)
Instead of doing all that, I would use the "New File" iPhone UI template to create a TableViewController with xib file option checked. Then you get a controller and xib file all wired together properly.
Eagle, when you create a new file, select the "UIViewController subclass" icon. There's a checkbox to make it a UITableViewController subclass just above the checkbox to include XIB file.
You've got two places where your UITableViewController shows up in Interface Builder.
(1) It shows up in the nib with the controllers own name.
(2) It shows up as a controller object in the nib of another object, usually the MainWindow.
Your problem is at (2). There are two ways to set the tableview for a UITableViewController in Interface builder. First, you can create a UITableView under the controller in the MainWindow and connect that to the controller's view property. Secondly, you can bring up the inspector in the attributes pane and in the popmenu listed "NibName" select the name of the controllers nib.
You can't use both systems at once because the first loads a view from the MainWindow nib file and second loads a completely unrelated view from the controller's separate nib file.
This is one of those maddening errors that using Interface Builder makes so hard to track down.