iphone - Interface Builder not loading custom class? - iphone

I generally don't use Interface Builder (I hate it). But I am being forced to use it, because I was invited to a project where people are using it. I am trying to create a custom UISlider.
I have created a UISlider custom class, with my own images for the slider parts.
If I add a new object to my main code using
mySlider *one = [[mySlider alloc] initWithFrame:CGRectMake(0,0,60,30)];
I see the slider as I created, beautifully.
But if I use this class on interface builder, to change the appearance of a UISlider I create there, the slider continues to have the same appearance as before and when I run the app the interface shows the slider with the default appearance, not the one I designed.
I created the slider on IB, simply dragging a UISlider on the interface and changing its class to the one I've created. Is there something else that has to be done? Why is it not showing as defined on the custom class?
thanks

You need to implement the initWithCoder: initializer. initWithFrame: will not be called when a nib is loaded.

Related

Create custom action in a class for use in Interface Builder

I want to create a custom action connection in a class, which should be visible in Interface Builder. For example - I add action / target properties to NSView class just like this:
weak open var object: AnyObject?
open var something: Selector?
The action is something and the target is object.
Now I want in Interface Builder to have 'Send Action' link / connection available for something and to be able to make connection to a #IBAction method to some class (for example the controller of the view), just like it can be done for a simple NSButton. Maybe this is not possible, or maybe I must add some keywords in front of the custom action / target pair, the same way we need to make a property #IBInspectable to appear in Attributes Inspector.
Any help is welcome ;-)
I don't think you can do that. You can add #IBInspectable properties to custom NSView subclasses, but there is no selector or action data type for properties. The only allowed types are Boolean, Number, String, Localized String, Point, Size, Rect, Range, Color, Image, Nil.
Interface Builder has hard-wired support for handling the target-action properties of controls.
(BTW you should probably add the Mac OS tag to your quesiton. Most Apple traffic on this board is iOS-related.
EDIT:
As somebody said in a comment, if you're creating an object that has a target/action, it should probably be an NSControl, not an NSView. Controls are the object family that handle target-actions, and there IS a mechanism in Interface Builder for adding target/actions to controls.
In iOS, there is one somewhat cheesy option -- you could have an outlet to a UIBarButtonItem, and someone could then drop a standalone UIBarButtonItem in their xib or storyboard. The UIBarButtonItem can then have the target/action, and your class could then pull the target/action from there in awakeFromNib and assign the values to your own class. That approach may be replicable in MacOS using NSToolbarItem, but I have not tried. It's a kludgy option really, as it requires extra xib objects and may not be too obvious what is going on, but it can work. Basically, the UIBarButtonItem / NSToolbarItem would only be acting as a container for the target/action values.
If you can't subclass NSControl, it would seem the preferred option is to have a delegate, probably. That is usually fine, unless one controller needs to be the delegate for multiple instances of your class, in which case the delegate methods get more tedious. Another option is to have your class have a block property, which is performed upon the action, but blocks must be assigned in code and not set up in Interface Builder.

Add UIView at a UIButton

Here is the requirement of my application:
There is a calendar type view which will show Date, Day, Total work hours of that day. What I need, when I click anywhere in that particular square block then it redirects my page to a new view.
I am thinking to should add multiple button and UIView on that button.
That can solve my problem but it is little bit problematic. How should I to do this?
Please look at UITapGestureRecognizer for your view. I think this is what you're looking for.
Here is a link to a tutorial on using UITapGestureRecognizer
Create a subclass of UIControl and a XIB with a UIView in it. Set the class type for the UIView to your new UIControl (not the file owner but the UIView). Then in the UIControl in the initWithCoder: method create the display component and add to view. Then set up you properties that you need to populate those controls.
To use this new control place a UIView where you want it on your screen and size it. Change its class type (in the inspector) to your new subclass. Now you can add outlets and actions to this view just like you would a UIButton. The only downfall is can't set the display controls from the inspector, you'll have to do that in the program. But the good side is you get a similar interface to a button and you can make it look anyway you like.
Hope this helps.

how do I ensure a custom UIView I have is repositioned properly after an orientation change of the iPhone

how do I ensure a custom UIView I have is repositioned properly after an orientation change of the iPhone?
Background
As a parent view I have a UIViewController with an XIB
I have a custom UIView which is used in this parent (inserted in the XIB via Interface Builder). It programmatically at init creates some UIImageView's and adds them via "self addSubview", and then positions them via "self addSubview:imageView1" type approach
So the questions are how do I ensure I get the correct layout of these as orientation changes. Specifically:
Where do I trigger any such redrawing of the custom UIView? (e.g. is there a method in the parent UIViewController I should be using to trigger from?)
When the trigger occurs how do I request the redraw of the custom UIView? Do I need a custom method I create myself like "relayoutCustomView"? or should I be able to use an existing method in the custom UIView, in which case do I need to make sure in the custom UIView that my layout code is in a particular method?
thanks
how's this for the answer - seems to be starting to work, not sure if it's the best way:
separate out the layout code in my custom view and put in a separate method and make this public
this method will be called from within the custom view "init" method upon setup
also now however create an "didRotateFromInterfaceOrientation" method in the parent, and from within here also call into the new custom view layout method mentioned in [1]
noted it had to be "didRotateFromInterfaceOrientation", as with the "didRotateFromInterfaceOrientation" method it didn't work as the self.bounds result hadn't yet changed
How does this sound? I didn't use the "setNeedsDisplay" anywhere...

iPhone: Create a reusable component (control) that has some Interface Builder pieces and some code

I want to create a reusable component (a custom control) for the iPhone. It consists of several standard controls prearranged on a View, and then some associated code. My goals are:
I want to be able to use Interface Builder to lay out the subviews in my custom control;
I want to somehow package the whole thing up so that I can then fairly easily drag and drop the resulting custom component into other Views, without having to manually rewire a bunch of outlets and so on. (A little manual rewiring is fine, I just don't want to do tons and tons of it.)
Let me be more concrete, and tell you specifically what my control is supposed to do. In my app, I sometimes need to hit a web service to validate data that the user has entered. While waiting for a reply from the web service, I want to display a spinner (an activity indicator). If the web services replies with a success code, I want to display a "success" checkmark. If the web service replies with an error code, I want to display an error icon and an error message.
The single-use way to do this is pretty easy: I just create a UIView that contains a UIActivityIndicatorView, two UIImages (one for the success icon and one for the error icon), and a UILabel for the error message. Here's a screenshot, with the relevant parts marked in red:
I then wire up the pieces to outlets, and I put some code in my controller.
But how do I package up those pieces -- the code and the little collection of views -- so that I can reuse them? Here are a few things I found that get me partway there, but aren't that great:
I can drag the collection of views and controls into the Custom Objects section of the Library; then, later, I can drag them back out onto other views. But (a) it forgets which images were associated with the two UIImages, (b) there is a lot of manual rewiring of four or five outlets, and (c) most importantly, this doesn't do bring along the code. (Perhaps there's an easy way to wire up the code?)
I think I could create an IBPlugin; not sure if that would help, and it seems like a lot of work, and also it's not entirely clear to me whether IBPlugins work for iPhone development.
I thought, "Hmm, there's code associated with this -- that smells like a controller," so I tried creating a custom controller (e.g. WebServiceValidatorController) with associated XIB file. That actually feels really promising, but then at that point I can't figure out how, in Interface Builder, to drag this component onto other views. The WebServiceValidatorController is a controller, not a view, so I can drag it into a Document Window, but not into a view.
I have a feeling I'm missing something obvious...
Here is how I'm solving a similar problem: I wanted to:
create reusable, self-contained "widget module classes" implementing a complex view built from multiple UIKit components (and other nested widget module classes!). The higher-level customer classes of these widget classes don't care about what's a UILabel, what's a UIImageView internally within the widget, the customer classes only care about concepts like "displaying the score" or "showing the team logo."
within a widget module class, lay out the UI for the widget and hookup outlets using interface builder
for higher level customers of a widget, I wanted to be able to place the frame of the widget within the view of the customer in interface builder without having to design custom plugins to IB, etc.
These widgets are not top level controllers: so it makes no sense for them to be subclasses of UIViewController. Then also there's the Apple advice not to have more than one VC on a visible screen at a time. Also, there's so much advice and opinion floating around like "MVC! MVC! You must separate your View and your control! MVC!" that people are so strongly discouraged from subclassing UIView or ever placing app logic within a UIView subclass.
But I decided to do it anyway (subclass UIView). I've been around the block a few times (but fairly new still to the iPhone SDK/UIKit), and I'm very sensitive to design that is ugly, and that can cause problems, and I frankly don't see the problem with subclassing UIView and adding outlets and actions. In fact there are many advantages to making your reusable widget be a subclass of UIView rather than be UIViewController-based:
You can place a direct instance of the widget class in a customer view's interface builder layout, and the widget view's UIView frame will be properly initialized when the customer class is loaded from the xib. This is much cleaner to me than putting a "placeholder view" in the customer, then instantiating the widget module programmatically, and setting the widget's frame to that of the placeholder, and then swapping the placeholder view for the widget's view.
You can create a clean and simple xib file to lay out the widget's components in interface builder. The File's Owner is set to your widget class. The nib file contains a root (vanilla) UIView where all of the GUI is laid out, and then in the awakeFromNib: method of the widget, this nib view is added to the widget itself as a subview. Any frame size adjustments can be handled here, entirely within the widget code, if any is necessary. Like so:
- (void) awakeFromNib
{
[[NSBundle mainBundle] loadNibNamed:#"MyWidgetView" owner:self options:nil];
[self addSubview:self.view];
}
The widget self-initialzes its user interface by using NSBundle's loadNibNamed:owner:options method in its own awakeFromNib method, so integration of the widget into customer classes is clean: just drop a UIView into the customer's view in IB, change the UIView's class to MyWidgetView, and in the customer's init or viewDidLoad set up any defaults in the widget's display using the widget's API that you write yourself (setScore: setTeamImage: etc.)
It seems to me there is essentially no difference in the resulting code between subclassing a UIView in this way to make a reusable "widget", and using a UIViewController. In both cases the .h files contain widget class members, outlets, action declarations, and special API declarations. In both cases the .m file contains action implementations, and special API implementation. And the view IS separated from the control -- the view is in the xib file!
So, in summary, this is what I do for packaging up complex reusable view widgets:
Make the widget class a subclass of UIView
Instantiate the widget wherever you want, in other views in your app in IB directly by dragging a UIView from the tools library, and changing the class to your "MyWidgetClass."
Design your widget's UI in IB in a nib inside a root vanilla UIView.
in the widget class's awakeFromNib: method, load the widget's UI from xib and do
[self addSubview:self.theXibRootView]
Code the widget just like you would a UIViewController: outlets, actions, special APIs
I'd be interested in hearing about other structural systems for creating reusable widgets, and what the advantages are over this approach.
If the widget needs to report events to the customer class, just use a delegate protocol where the customer sets the widget's delegate to self, just like in a UIViewController.
I've created similar constructs except that I do not use the result in IB, but instantiate using the code. I'll describe how that works, and at the end I'll give you a hint how that can be used to accomplish what you're after.
I start from an empty XIB file where I add one custom view at the top level. I configure that custom view to be my class. Below in view hierarchy I create and configure subviews as required.
I create all IBOutlets in my custom-view class, and connect them there. In this exercise I ignore the "File's owner" completely.
Now, when I need to create the view (usually in controller as part of while/for-loop to create as much of them as needed), I use NSBundle's functionality like this:
- (void)viewDidLoad
{
CGRect fooBarViewFrame = CGRectMake(0, 0, self.view.bounds.size.width, FOOBARVIEW_HEIGHT);
for (MBSomeData *data in self.dataArray) {
FooBarView *fooBarView = [self loadFooBarViewForData:data];
fooBarView.frame = fooBarViewFrame;
[self.view addSubview:fooBarView];
fooBarViewFrame = CGRectOffset(fooBarViewFrame, 0, FOOBARVIEW_HEIGHT);
}
}
- (FooBarView*)loadFooBarViewForData:(MBSomeData*)data
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"FooBarView" owner:self options:nil];
FooBarView *fooBarView = [topLevelObjects objectAtIndex:0];
fooBarView.barView.amountInEuro = data.amountInEuro;
fooBarView.barView.gradientStartColor = data.color1;
fooBarView.barView.gradientMidColor = data.color2;
fooBarView.titleLabel.text = data.name;
fooBarView.titleLabel.textColor = data.nameColor;
return fooBarView;
}
Notice, how I set owner of nib to self - there's no harm as I didn't connect "File's owner" to anything. The only valuable result from loading this nib is its first element - my view.
If you want to adapt this approach for creating views in IB, it's pretty easy. Implement loading of subview in a main custom view. Set the subview's frame to main view's bounds, so that they are the same size. The main view will become the container for your real custom view, and also an interface for external code - you open only needed properties of its subviews, the rest is encapsulated. Then you just drop custom view in IB, configure it to be your class, and use it as usual.

Best way to use custom TTButton with interface builder

I am quite new to the whole IPhone development thing and was playing around with the Three20 library. The samples for this library showed me an easy way to create a nice looking button with styles etc and this to my view:
TTButton* button = [TTButton buttonWithStyle:#"forwardActionButton:" title:#"Login"];
[button setFrame:CGRectMake(245, 160, 65, 33)];
[self.view addSubview:button];
Works great. Now I have created a nib file in interface builder with my whole view layed out including all sorts of textfields etc.
What I would like to do is, is to create a placeholder for a button in Interface Builder and in my viewDidLoad method replace this placeholder with the actual TTButton instance. This would prevent my from having to call setFrame with an hardcoded location and instead make the whole interface design process much more free.
I have already tried creating a UIView object in interface builder, changing the type to TTButton and then assigning a new instance but seems to put the new button at coordinates location 0,0.
Can anybody point me in the right direction on how to achieve this?
waseem,
Unfortunately three20 does not use interface builder at all. The best thing you can do is layout a mock-up view and grab button x-y locations from that, then use those to update your TTButton locations in code.