Creating custom class and adding to my viewcontroller from storyboad? - swift

I'm just copying codes from an old project. I found something strange and I don't know how to do that. I just want to create that "Chart View Model" like below images. I also need an outlet too.
There is also a corresponding class.
class ChartModel: NSObject {
// .....
}
All I want to know is the purpose of using model in storyboads and how to do that.

Have you ever put a gesture recognizer into a storyboard scene? This is exactly parallel. The loading of the nib instantiates the gesture recognizer and attaches it to a view, so that you don't have to do that in code. That's what's happening here.
Any NSObject subclass can be instantiated as a nib object. Find the Object in the library:
Drag it from the library right into the scene; select it and change its class in the Identity inspector to the desired class (ChartModel).
Now you face the problem of what will happen to this instance when it is created at nib-loading time. On iOS, it will vanish in a puff of smoke unless someone else retains it. The usual solution is that you've got some other nib object with an outlet to this Object. Now, when the nib loads, the Object is instantiated and assigned to the corresponding property in the other nib object. That is what your outlet does:
#IBOutlet var chartViewModel : ChartModel!
Okay, but so far, that is exactly equivalent to saying
var chartViewModel = ChartModel()
It's just that, instead of instantiating the ChartModel in code, we instantiate it by the loading of the nib.
So why do that in the first place? Why instantiate this ChartModel from the nib instead of in code? It makes sense only if a ChartModel itself has outlets that can be configured in the nib. You didn't show us that (you only showed the first line of the declaration of class ChartModel) so it's impossible to say more about what the actual purpose was in this case.

Related

how to update label/textfield of storyboard uiviewcontroller from controller.m?

I am new to iphone development, and i'm currently using xcode 4.2.
I drew a UIViewController into the storyboard. I put some labels into the UIViewController. It is associated to a controller.m file.
How do I, from the viewdidload function of the controller.m file, update the text in the label?
I don't know how to get the variable name or handle name of the elements in the uiviewcontroller on the storyboard.
If you are creating things through storyboard, the easiest way is to create an IBOutlet property for each UI element. This creates an instance property for the UIElement which you can then reference in your code - to set state, get values, etc.
You can do this through Control-click-drag on the UIElement to your UIViewController.h file (split view: RETURN-Command-Option: to display counterparts). XCode will pop up a dialog enabling you to name it (the same gesture will also enable you to create IBActions - the function that is called when the UIElement gets interacted with). You can also Control-click-drag on the left sidebar listing of the UIElements in your ViewController if that is easier.
There are YouTube videos that show it better than my weak explanation. I was skeptical # storyboard/IB at first because I don't like UI magic and prefer to do things through code, or at least see the code that results from the magic, but it really does work pretty well and saves some of the tedium of UI coding.
One gotcha to be aware of: if you make an IBOutlet and then delete the UIElement, you will have code errors because of the errant reference. Those are easy to find and fix. The UIViewController object will also contain those references and will result in unpleasant crashes - so Control-click on the UIViewController object (or use the Inspector panel) and any element that has an orange triangle == broken.

UIImageView Created In Interface Builder Set To nil When Needed

I have an application where small number of objects are defined in the MainWindow. A pair of these objects are a UIViewController and the UIImageView that goes along with this controller. When the application starts up, the entire MainWindow.xib get loaded & I can see that this UIImageView has been loaded into memory. At this point, I have no need for this UIImageView.
When I need the UIViewController, I perform the usual alloc/init setup. At this point, when I look at this controller with the debugger, the controller is setup correctly, but the UIImageView that should have been pre-wired with Interface Builder is always nil.
Any suggegstions on how to make sure this UIImageView is loaded into memory correctly? This is the only case where I've had an issue with objects defined within this single MainWindow.xib file.
Assuming you have connected the view outlet, all you need to do is access the view property. When you look in the debugger, you're seeing the property's corresponding ivar. When you access the property in code, it should load for you. So, it should be as simple as:
[controller view];
Not quite sure I understand the problem, but instead of having the UIImageView in MainWindow.xib, why not have it in the xxxViewController.xib where it's actually going to be needed?

Subclass of UIViewController initialising itself from a .xib can't use UINavigationItem?

I've finally had to give up the relentless search for an answer to this question, as I just can't find anyone that's asked it before! So hope someone can provide some insight. I'll start by explaining what I can do, then compare that with what I can't figure out how to do.
Suppose I have a custom VC called RootViewController. It contains an outlet of type MyViewController. My RootViewController has an .xib which contains a generic VC object dragged out of IB's palette which is given the class type of MyViewController, I set the right bar button item to a UIBarButtonItem called 'Cancel'. The RootViewController unarchives it's .xib, hooks up the outlet to the MyViewController object, I push said object on to the navigation stack which displays as expected, I have a view and a button on the navigation bar that says 'Cancel'.
The problem I have with this approach is that I want MyViewController to be re-usable across any object that might want to create a MyViewController. This therefore means that the "File's Owner" could be any object. Previously it was RootViewController, but what if another object wanted to instantiate a MyViewController? The Files Owner will be different every time. I also want it to be able to completely initialise itself from a nicely self-contained .xib. Basically, I want to do this:
MyViewController *myVC = [[MyViewController alloc] init];
And in the implementation of MyViewController, I write this:
- (id)init
{
if ( self = [super initWithNibName:#"MyViewController" bundle:nil] )
{
// Initialisation
}
}
This neatly hides the name of the .xib used to intialise the VC, uses all the goodness IB gives me in configuring the controller and view, and with MyViewController always being the owner, it means I solve the File's Owner problem too - it will work for any type of class that creates it.
So, in order to achieve this, I create a .xib, set the File's Owner to be of type MyViewController, drop in a UINavigationItem and add the UIBarButtonItem. I now have a .xib structurally the same as before, except it does not use IB's generic VC object as separate top level object, the .xib is the VC definition, rather than something that contains a VC definition.
So, given that File's Owner is a MyViewController and as such a subclass of UIViewController (supposedly inheriting everything you get by using a UIViewController from IB's palette), then I should inherit all the functionality of it's superclass... Except it doesn't.
When the .xib in unarchived, it does not hook up the UINavigationItem. Therefore, when it's pushed on to the navigation stack, none of the bar button items are displayed.
UIViewControllers navigationItem property is read-only, creating an outlet for it in iPhone OS 3.0 is therefore deprecated.
So, at the end of all this, how on earth does the nib loading code manage to connect IB's version of the VC object to the navigation item? And why, even though my object is a UIViewController through inheritance, will it not do it for my object? I am completely at a loss to fathom this out...
Thanks for reading this far! Hope to hear from you guru's
Only a UINavigationController sets UIViewController's navigationItem property. Do something like this:
MyViewController *viewController = [[MyViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
Assuming you are in a view controller that already has a navigation controller. After you have pushed it, you will be able to access navigationItem from inside MyViewController.
Right, I think I get it now. I thought I'd post an answer to my own question as I've seen at least one other person ask a similar question, and both seem to point to the same conclusion, so I hope this will be of help to others.
Essentially (and this will sound obvious) a UIViewController is not a Proxy Object! It's not instantly obvious as we're all used to the idea that if any two objects inherit from the same base class, then their implementations are the same and they will behave in exactly the same way (assuming no customisation in the inheriting class). But an IB object's type is distinct from the class attribute you can assign to these objects.
They are not both UIViewController objects because their class attribute it set in such a way.
Simply setting the class attribute of these objects in IB does not mean that these objects are now UIViewController objects. A View Controller object remains a View Controler object, and a Proxy Object remains a Proxy Object. As far as IB is concerned, they are both very different beasts, they just happen to have the same class attribute.
Just take a look in to the .xib and you'll find your IB View Controller objects have been archived like this:
<object class="IBUIViewController" id="...">
...
</object>
And the Proxy Object (that is set to a subclass of a UIViewController) is archived like this:
<object class="IBProxyObject" id="...">
...
</object>
As you can see they are both very different types, one is a IBUIViewController and the other is a IBProxyObject - and then it starts to makes sense - you can't impose VC controller attributes on an object of type IBProxyObject.
It is also interesting to note the class type is an IBUIViewController object and not just a UIViewController object. Whether this is just a naming convention or not I don't know, but it could also imply that IB's view controller objects wrap the instantiated VC object, or is a factory object for it. For example, you can set a "Resize View From NIB" attribute in IB's view controller object - but I can find no equivalent property or methods in the UIViewController reference docs.
So in conclusion, if you're trying to instantiate an object programatically instead of using outlets to an IB object, be prepared to implement some of the initialisation that the IB version would otherwise provide for you...

iPhone programming, how objects work?

Another noob question, but while looking at some code examples, I see stuff like
UIViewController *controller = [[UIViewController alloc] init...
Making an instance of the class UIViewController which I understand. But then you have these instance variables of your own class. Like let's say I was building a simple counter program called Counter and it counts when you touch the screen and I have a property for my view controller called "count." So when I run my program, is that when my instance of my class is created? Because it's not like I ever create a Counter *counter object. Thanks.
So when I run my program, is that when
my instance of my class is created?
Yes, when your program executes the objects are created.
If you have allocated and initiated counter but are using it, I'm surprised anything works.
You allocate and initialize objects before you use them and then release them when you are done.
I think you may be confused because of the way loading nib (XIB) files instantiates objects without any actual Objective-C code to instantiate the object.
Read about the NIB Object Lifecycle in this guide: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW18
Basically, the first NIB gets loaded based on whatever is specified in the YourAppName-Info.plist for "Main nib file base name" and that will instantiate any objects defined in there and then set IBOutlets to point to them as specified in the NIB file. As a result, certain objects will seem to just "be there" referenced in the outlets (like IBOutlet UIWindow *window in your app delegate) by the time your code executes.
I recommend the book Beginning iPhone 3 Development as a great tutorial for all of these things.

Arbitrary objects to handle actions?

My question may be a bit stupid, but why can't any object instantiated in IB handle, say, button click? I mean I'm able to add my own object to a xib, and link outlets to cotrols and control actions to object's method, but once I press the button everything just crashes (uknown selector).
Do you guys have a hint around that?
EDIT: The code, as requested:
#interface TextController {
IBOutlet UILabel * textLabel;
IBOutlet UITextField * textField;
}
-(IBAction)buttonClicked:(id)sender;
#end
#implementation TextController
-(IBAction)buttonClicked:(id)sender {
textLabel.text = #"Ololo";
}
#end
Connections in IB are ok, just believe me. It's really hard to get them wrong with all this drag'n'drop stuff :)
EDIT 2: TextController is not a file owner (in this case it works fine). However, I just want to understand why I can't wire up an action to some object (may be even not a subclass of UIViewController).
You can wire outlets and actions to any object in the nib-file. Drag an NSObject form the library palette onto your nib-file, in Interface Builder. Then go to the Identity tab of the information palette and set the Class of your object.
This way you can instantiate any object of any class from your nib. If the target you want to hook to is statically created from the nib-file. Make sure that the file's owner have at least one reference to your object, or else it will be deallocated as soon as it has been created. Targets are not retained by the sender.
If the object you want to hook up should not be statically created from your nib, then implement awakeFromNib in a class that is instantiated from the nib-file and hook up the targets in code.
Last option is if you do not have any sub-class of your own in the nib-file at all. Then implement initWithNibName:bundle: in your UIViewController subclass, and hook up your targets in code after calling the super implementation.
post code, this usually means you dont have your connections wired up correctly. Is file's owner TextController in IB?