iOS: how to get access viewController's methods - objective-c

I have a trouble.
There is viewConntroller and an object inside this viewController. This object tries to get information via NSURLConnection later it has to notify viewController to run method. If i try to create object of viewController inside I catch problem: new viewController makes the same thing that first one do, so new viewController makes NSURLConnection, creates third viewController and so on.

You can pass reference to this object. But first create new property with assign attribute.
//property in your class
#property(nonatomic, assign) id myViewController;
//set reference to your UIViewController
[myClassInstance setMyViewController: self]; //self is MyViewController instance
//call UIViewController's method from your class
MyViewController *controller = (MyViewController *)self.myViewController;
[controller myMethodInUIViewController];

How about passing on a reference of the viewController to the object you´re making inside that viewController?
So something along the lines of:
Object *obj = [[Object alloc]init];
obj.viewController = self;
Obviously, you'd have to create an attribute to hold the viewController on the object class.
Then, when you need to call methods on the viewController, you could use:
[self.viewController performSelector:#selector(methodName:)];

Related

setHidden with NSSlider doesn't work - Objective C

Hy guys, I'm new at ObjC and I'm still learning;
[sliderContrast setHidden:YES] (i also used slider.hidden = YES) doesn't make the slider invisible, instead it works fine with textfields. Do you know why?
I've also tried using property and synthesize but the result doesn't change
---Interface
#interface Controller : NSWindowController{
IBOutlet NSTextField *labelContrast;
IBOutlet NSTextField *valueContrast;
IBOutlet NSSlider *sliderContrast;
}
- (IBAction)changeContrast:(id)sender;
#end
---Implementation
#import "Controller.h"
#interface Controller ()
#end
#implementation Controller
- (void)windowDidLoad {
[super windowDidLoad];
[labelContrast setHidden:YES];
[valueContrast setHidden:YES];
[sliderContrast setHidden:YES];
}
- (IBAction)changeContrast:(id)sender {
}
#end
If you declare pointers for your objects but you don't allocate them yourself you can not set anything that is not there. Your setHidden: method calls end up in local void aka nowhere.
programmatically
If you go the coding way you would declare, allocate and initiate first. With
labelContrast = [NSTextField alloc] initWithFrame:NSMakeRect(x,y,w,h)];
before you call other methods of the object class (Or similar init methods).After that you can call methods on the object.
Almost all objects inherit an according -(instancetype)init or -(instancetype)initWith... method you can use. If there is no init method given, then there is another way to do it right and the moment to start reading again :).
With Interface Builder
By typing IBOutlet or IBAction in front of a declaration you just give a hint for Xcodes Interface Builder where to hook up and apply onto (associate) the placed object in (nib,xib,storyboard) with its object ID in the XML scheme to a reference in code.
So after you connected your code and the object in IB you can avoid allocation and init process for that particular object. Be aware that calling methods on objects that are not instanced yet is not working. Which is why you code in - (void)windowDidLoad, -(void)viewDidLoad or -(void)awakeFromNib because those are the methods that get called after "IB" has done its job for you.

IBOutlet is nil after initWithCoder is called

Simple problem, I have defined a UIImageView, called bigImageView in a UIViewController using the storyboard,
It's declared in the h file of that UIViewController as follows:
#property (retain, nonatomic) IBOutlet UIImageView *bigImageView;
on my appDelegate I init the UIViewController as follows:
imageViewController = [storyboard instantiateViewControllerWithIdentifier:#"chosenImageController"];
this calls initWithCoder on my UIViewController m file:
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// Custom initialization
}
return self;
}
This function is only called once so there's no double init.
However, later, when I check my bigImageView pointer, it's still nil.
Isn't the init supposed to allocate memory to it?
I think that's why when I try to set this UIImageview to hold a UIImage it doesn't display the image
Thanks
It's all working how it's meant to. First every object in the nib/storyboard gets alloc/init called on them, then all the connections are made, and then viewDidLoad is called.
You need to wait for - (void)viewDidLoad to be called on your controller, and then bigImageView should be set. If it's not set then you did something wrong in the storyboard.
init methods are not responsible for allocating any memory. All memory is allocated by the alloc method which is always called before init. Alloc will fill all your instance variables with nil/NULL/0 values, and then init gives the chance to assign initial values to each one (based on the contents of the NSCoder object usually, but it's up to you to decide what should be done).
For IB outlets however, those are setup by the nib loading process after init.
EDIT:
// ViewControllerA.m:
imageViewController = [storyboard instantiateViewControllerWithIdentifier:#"chosenImageController"];
imageViewController.image = imageToShow;
// ViewControllerB.h
#property (retain) NSImage *image;
#property (retain, nonatomic) IBOutlet UIImageView *bigImageView;
// ViewControllerB.m
- (void)viewDidLoad
{
self.bigImageView.image = self.image;
[super viewDidLoad];
}
You don't need to define initWithCoder, since you have no custom logic in there. I would delete that boilerplate code.
Here is what I would check:
In the storyboard, ensure that the class of the view controller is set properly.
Ensure that the outlet is hooked up properly in the storyboard by looking for a circle near your #property. It should be a filled in circle, not an outline of a circle.
Make sure you are reading the value only after viewDidLoad is called. Apple's only guarantee is that the outlet is set after this method call.
Update: It sounds like you want to access the image view before the view is loaded. There is no way to do this. One hack is to call viewController.view which will force the view to load, but there are many reasons why you should not do this.
A better approach would be to implement properties on your view controller which work for both when the view is not loaded and when the view is loaded. You can see an example of an elegant solution in this question. Notice how if the view is loaded, the photographerLabel will get set via the didSet method. On the other hand, if the view is not loaded, it will get set via the viewDidLoad method. For an Objective-C version of that code or for more details, see the linked video in that question.

NSLogging UILabel's text outputs null

I have a custom view controller called TimerViewController and subclasses of this called FirstViewController and FourthViewController.
I declared an instance of FirstViewController in FirstViewController's .h named controller.
In the viewDidLoad method of FourthViewController's .m, I have:
controller = [self.storyboard instantiateViewControllerWithIdentifier:#"mainController"];
In the storyboard, I've declared the view controller ID as mainController and declared a custom class of FourthViewController. Then, in FourthViewController's .m I have:
controller.mainLab.text = [NSMutableString stringWithFormat:#"This is a string"];
NSLog(#"%#", controller.mainLab.text);
This, however, outputs (null).
Why does this happen?
mainLab must be nil. So your outlet probably isn't connected to your XIB.
As an aside, using stringWithFormat: with a string that isn't a format is wasteful.
Your neglecting to tell us something about the rest of your project, im just not sure what it is.
I fired up Xcode just to run through this real quick and the process is simple.
Drag UI Label to your XIB
Control click from label to your .h
For testing I did
#import "SOViewController.h"
- (void)viewDidLoad
{
[super viewDidLoad];
self.mainLabel.text = #"This is my label";
NSLog(#"%#", self.mainLabel.text);
}
My .h looks like this:
#import <UIKit/UIKit.h>
#interface SOViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *mainLabel;
#end
Is this part of a custom class? Is there something else going on? If its a vanilla label it should work without issue using the aforementioned code.
it looks like your mainLab has not yet been created. When you call methods on nil objects, the method automatically returns nil. Make sure you actually create the label before running this line of code.
You can't access the label (or any other UI element) of another controller right after you instantiate it, because its viewDidLoad method has not yet run. If you want to set the text of a label in another controller, you have to pass the text to that controller, and have it set the text on the label in its viewDidLoad method. So instead of the this:
controller = [self.storyboard instantiateViewControllerWithIdentifier:#"mainController"];
controller.mainLab.text = [NSMutableString stringWithFormat:#"This is a string"];
You need to do this:
controller = [self.storyboard instantiateViewControllerWithIdentifier:#"mainController"];
controller.mainLabText = #"This is a string";
where mainLabText is a string property you create in FourthViewController. Then populate the label in FourthViewController's viewDidLoad:
self.mainLab.text = self.mainLabText;

How to set properties of an object, for which Xcode is unaware exist, but do

Edit - I've revised to clarify.
//myViewController.h
#property (strong, nonatomic) SCDataObject *dataObject;
In storyboard, I've created a single VC with the custom class myViewController. I've given it the storyboard ID myViewControllerStoryboardId.
//anotherClass.m
UIViewController *viewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"myViewControllerStoryboardId"];
I instantiate the custom VC, but notice I'm setting it to a pointer of type UIViewController on purpose for a few reasons.
//anotherClass.m
//I want to set the property, dataObject, of the instantiated VC, but this doesn't work.
viewController.dataObject = something;
The actual object has the property, but the pointer to it is of a different class. How do I set the property?
You need to use your viewcontrollers class. I used MyViewController, just replace it with your class name. Here is the code:
#import "MyViewController.h"
...
MyViewController *viewController =
[self.storyboard
instantiateViewControllerWithIdentifier:#"myViewControllerStoryboardId"];
//It works!
viewController.dataObject = something;
EDIT:
If you really don't want to use your custom class, give this code a shot:
[viewController setValue:something forKey:#"dataObject"];
But i can't think of a reason for doing this.
if myViewController is a subclass of UIViewController then you should really do:
myViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"myViewControllerStoryboardId"];
You also need to make sure that you change the class of your custom UIViewController in your Storyboard from the generic UIViewController to myViewController (select your VC in the StoryBoard and then go to the "Identity Inspector", that is the 3rd tab, in the right-hand Utilities screen. You can change there the class of the custom VC to your subclass).
Update after Question Edit:
The UIViewController class is part of UIKit. Anytime you need to customize a VC beyond what can be done in SB, you need to subclass UIViewController (and create a custom VC). So if you are trying to have a custom property (dataObject) of a UIVC without subclassing UIVC, the answer is you can't.
Having said that, maybe the question is not fully clear. If you are looking to have a subclass of UIVC with a property dataObject but with multiple instances of that subclass; you can always create that subclass once and then have multiple instances of that class that you would give different names so:
myViewController* viewController1 = [self.storyboard instantiateViewControllerWithIdentifier:#"myViewControllerStoryboardId"];
myViewController* viewController2 = [self.storyboard instantiateViewControllerWithIdentifier:#"myViewControllerStoryboardId"];
myViewController* viewController3 = [self.storyboard instantiateViewControllerWithIdentifier:#"myViewControllerStoryboardId"];
They will all be different instances of the same subclass (myViewController) and the values of the dataObject will be distinct for each instance. In other words, you can set:
viewController1.dataObject=someObject;
viewController2.dataObject=anotherObject;
If you just type viewController as id, though you still can't use the property directly, you could send it a message the corresponds to that property, without warnings. You could use respondsToSelector: to see if it supports the accessor function for that property.
id viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"myViewControllerStoryboardId"];
if( [viewController respondsToSelector:#selector(setDataObject:)] ) {
[viewController setDataObject:something];
}
Personally, I would probably just:
MyViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"myViewControllerStoryboardId"];
viewController.dataObject = something;
You will get warnings, but this should work for setting:
[viewController setDataObject:something];
And this for getting:
[viewController dataObject];
To avoid a crash, you probably would do something like this:
if ([viewController respondsToSelector:#selector(setDataObject:)])
{
[viewController setDataObject:something];
}

property loses value in IBAction

I am setting the parent property for a window and when I check the property in windowDidLoad, everything is good. But when I check it in IBAction, it is nil. What am I missing here?
AppController.m
loginController = [[LoginController alloc] initWithWindowNibName:#"Login" owner:self];
loginController.parent = self;
[[loginController window] makeKeyAndOrderFront:self];
LoginController.h
#property (nonatomic, weak) AppController *parent;
LoginController.m
#synthesize parent;
- (void)windowDidLoad
{
[super windowDidLoad];
NSLog(#"Parent: %#", self.parent); //<--- Parent: <AppController: 0xblahblah>
}
- (IBAction)login:(id)sender
{
NSLog(#"Parent: %#", self.parent); //<--- nil
}
I think that your problem is just that you have set the wrong file owner, is login controller that holds the xib file, thus it has all the IBOutlets and IBActions bound.
Call initWithWindowNibName: instead of initWithWindowNibName:owner: , so that the file owner will be the newly created login controller, and not the app controller:
loginController = [[LoginController alloc] initWithWindowNibName:#"Login"];
Edit
Like I suspected, you have two separate instances of login controller and you think to have only one. See the xib file:
That object "Login Controller" in the xib file, creates another instance of login controller. It's not the same instance that you allocate in app controller.
The solution is to make parent be an IBOutlet:
#property (nonatomic, weak) IBOutlet AppController *parent;
And to don't allocate it in app controller, it will be load from the xib file automatically. All you have to do is to bind it to the instance of login controller in the xib file (if the file owner is app controller you should ctrl-drag the parent propert to the object icon, tell me if you meet some problem doing it). So that's why it prints null: the action is handled by another object, which hasn't the parent property initialized.
In viewDidLoad you access an iVar called parent.
In login: you access the property self.parent.
That can be the same or it can be different.
Did you #synthesize the property? If so, without any additional options? Then you should be fine. parent and self.parent should be the same thing.
Did you autosynthesize it? That is ok. But then the corresponding iVar is called _parent. Meaning then you must have an additional ivar parent which is not the same ivar.
Add all the declarations to your question for more details.