UIView or UIViewController for custom View - iphone

What is the best way to create a custom UIView that I can consume in Interface Builder?
Create a custom UIView in Interface Builder and inherit from UIView in a code file, then somehow use it in another UIView ala like a control. (How do I do this?)
Create a custom UIView in Interface Builder and have a custom UIViewController wire it up. In my main ViewController, place the new view.
Basically, I am trying to create a reusable display view and would like a quick way to change it across all my instances with minimal effort. I already have laid out my XIB in Interface Builder.

The best is the 1st way. And don't forget to place IBOutlet keyword before class member, that you want to see in Interface Builder.
#interface MyViewController : UIViewController {
IBOutlet UILabel *m_MyLabel;
}
....

You'll want to do (1). Presumably you've got an existing IB file in which you'd like to place the custom UIView subclass? In that case, go to that file, drag out a UIView, and then in the "Application Identity" tab of the inspector (4th tab) set the Class to your custom class (as defined in code).

Related

Interface Builder won't allow connections to custom UIView class?

Using Xcode 4.3.3, I can't figure out how to connect outlets in a custom UIView class with objects created in Interface Builder.
In one ViewController, I have a variety of buttons, sliders, etc that I'm trying to group into Views. So, within that ViewController in IB, I added 3 views. Only 1 view will be visible at any given time.
I derived custom UIView classes to handle each of these 3 views. My view controller instantiates each of the classes. I selected the view(s) in IB, opened the Identity Inspector and set the Class(es) to my custom class(es). But when I tried dragging connections from the view and/or it's constituent controls to the custom view's .h file IB won't add a connection.
IB allows me to add outlets/actions by dragging to the parent view controller's .h, but not to the custom view's .h file. I thought once I set the View's class to be my custom class, I could drag outlets for the view's components to my custom class rather than in the view controller.
This question seems identical to mine: how to connect UIview outlets to a custom subview The two solutions (manually adding outlets and setting the view's class in IB) did not change the behavior for me. Here are the manual outlets I added:
customView3.h
#import <UIKit/UIKit.h>
#interface customView3 : UIView
#property (retain) IBOutlet customView3 *view3;
#property (retain) IBOutlet UISlider *slider;
#end
customView3.m
#import "customView3.h"
#implementation customView3
#synthesize view3, slider;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
#end
What am I missing here? What else do I need to set/check in order to add outlets to my custom UIView rather than the view controller?
Note that I did get this to work today, but had to insert/type the outlets by hand in the derived class, then I could drag from the header file to the UI element in the Storyboard, but not the other way around.
I opened a bug with Apple. If anything interesting happens, I'll update this answer.
Updated 10/4/13: After much research, it seems the root of the problem is that IB believes that I'm trying to connect objects to properties within a class, that I'm going to then create a swarm of instances of this class and then IB won't be able to tell which instance should handle the messages. So, it disallows the connection on the belief that I'm (potentially) making simultaneous connections from one object to multiple copies of a property across several instances of the class.
My view is that I only want a single instance of the custom UIView, and that I should be able to tell IB, "don't panic, you're only dealing with this one instance."
I'm now focused on a new project and haven't had a chance to retry this in Xcode 5; however, given the info above, I'm not optimistic it will work in Xcode 5, either.
What you're trying to do is certainly both sensible (IMO) and possible. XCode is however quite quirky when it comes to establishing connections to outlets in subviews.
I've found that cleaning, rebuilding and sometimes restarting XCode tends to help, but sometimes it just doesn't work. In some cases I got it working by manually creating the outlet and dragging from the outlet to the control rather than the other way.
This is not very helpful, I know, but I just wanted to make it clear that this is supposed to work and when it doesn't it is most certainly due to a bug in XCode.
In my experience, you add IBAction and IBOutlet to subclasses of UIViewController and not subclasses of UIView. So within the MVC model, your "1" UIViewController has all of these IBOutlets in it and then you use the data from these IBOutlets to update your views.
So you are fine creating the UIViews objects in IB and then creating the corresponding classes in code and then changing the class of the UIViews objects in IB to your UIView subclass. Just put all IBOutlets in the UIViewController that has all the UIViews under it, get the values you need from the IBOutlets and then pass them down to (set them in) your UIView subclasses.
Hope this helps.
I had the same problem and found a workaround:
1) Open the Assistant Editor with customView3.xib on the left and customView3.h on the right
2) ctrl + drag from UISlider in customView3.xib to the customView3.h code on the right
3) An option to create a new Outlet connection will pop up.
4) Name the connection and click connect
It will then create a connected IBOutlet.

How to get a reference to a control in the view?

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)

Adding outlets to UIButton

For quickly mocking up UI, I'd like to be able to drag buttons onto a view in interface builder, then drag a connection from that button to the view that should appear when you click it.
A subclass of UIButton is a little inconvenient to use in IB, so I'd prefer to add the behavior to UIButton itself. Unfortunately, it seems like outlets created in a category aren't visible in IB:
#interface UIButton (myextensions) {
IBOutlet UIView *outletDestination;
}
#end
Can extra outlets be added this way?
Can extra outlets be added this way?
Nope. You can't add instance variables by declaring them in a category.
You can, however, add properties, and you can put IBOutlet on properties, so you can add outlets that way. With the modern runtime (the only one available on the iPhone), properties can add instance variables.
I don't think you can do this if you intend to make a custom accessor for the property (you must use #synthesize), but it doesn't sound like this matters for your case: You're just mocking up UI, so you're not going to write custom accessors.
Alternatively, you can create outlets in IB itself on the Classes tab of the Library panel. Select a class there, then the Outlets tab in the pane below, then add an outlet to the list.
You'll need to have a nib open for that other solution, or no classes will show up. That's because it's context-sensitive: A Mac nib will have AppKit classes (like NSButton), whereas an iPhone nib will have UIKit classes.

UIViewController with custom UIView

Is there a way to make e.g. UIViewController use a custom UIVIew class?
Yes, there is. Just create your custom class that inherits UIView, open interface builder, open the ViewController xib file, select the view, and change the Class on the View Identity tab.
You can create your custom view in the Interface Builder and reuse it in a Controller. I just found the answer at the bottom of this post:
http://fdiv.net/2011/10/29/reusable-views-ios
Download the example, there is no code in the files, only the connections used in IB and the skeletton of the custom view.

Custom UIView built with Interface Builder accessible/positionable via Interface Builder

This shouldn't be this confusing. I have a custom UIView with a bunch on controls on it. UILabels, buttons, etc. I've created this Nib using Interface Builder. I want to be able to position this custom uiview on another UIView using the interface builder.
How do I link my UIView custom class, to the nib? initWithCoder gets called, but I want this class to get loaded from the nib.
Thanks
For this to work, you have to create a plug-in for Interface Builder that uses your custom control's class. As soon as you create and install your plug-in, you will be able to add by drag and drop, instances of your view onto another window or view in Interface Builder. To learn about creating IB Plugins, see the Interface Builder Plug-In Programming Guide and the chapter on creating your own IB Palette controls from Aaron Hillegass's book, Cocoa Programming for Mac OS X.
Here is the link to the original author of the accepted answer to a similar question.
You only need to make a plugin if you want the custom view to draw correctly in the nib you are using it. You can make a custom control and then have it show as a blank rectangle until instantiated during run right now.
How do I get a view in Interface Builder to load a custom view in another nib?
You can insert a UIViewController object using Interface Builder, and then set the UIViewController's "Nib name" property.
I don't know if this messes up your model, but I think it's the only way to do what you're trying to do.
In IB bring up the Identity Inspector tool (Command-4) then select your custom view and in the Class pop-up choose the name of your custom class instead of generic UIView. You may want to connect it to an ivar as well. In your ViewController declare an instance of your custom class with an IBOutlet in front of it. Then go back and bring up the Connections Inspector and connect your view to the ivar by click-dragging from the custom-view's referencing outlet to the File's Owner (which should be an instance of your ViewController) and choosing the ivar name.
When your NIB is loaded it should be creating an object of that type and connecting it to that variable.