Accessing cocoa interface builder controller methods - objective-c

So I have an objective-c project that uses a controller class. This interfaces with interface builder via IBOutlets.
My understanding is that the controller gets initialized by loading the user interface (as it is added to interface builder as an object). I would then like to use the controller's getter methods to return values that are in the IBOutlet fields.
So, to clarify what I mean with some code, here's my controller interface/implementation:
#interface controller : NSObject {
#private
IBOutlet NSTextField *name;
}
-(NSString*) name;
#end
Then, in my implementation, I have:
-(NSString*) name
{
return [name stringValue];
}
in a third class, I'd like to be able to write:
NSString blahblah = [controller name] and have the value of blahblah assume the value of whatever is in the controller's IBOutlet "name" field.
Hopefully this makes sense. When I try and do it this way I get "Semantic Issue: Method '+name' not found (return type defaults to 'id')"
Why? Where is the controller object actually substantiated and where, and how do I access it's fields?

You have to instantiate the controller by using [[controller alloc] initWithNibName:nibName bundle:nil];
By just using [controller name] you are calling a class method. Also it should be NSString *blahblah
You should read this document:
http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/_index.html#//apple_ref/doc/uid/TP40007594

Related

Objective-C - Call a method that modify a textField from another class

I have two questions :
When I try to call a method from different class (this method modify a textfield after having checked a condition) the method is well called (the NSLog in statutInternet works) but the TextField isn't modified..
When I do it from the (IBAction)internet method it works .. Any solution ?
Why Xcode want me to called my variables (like internetTextfield) with a _ before it ?
WindowsController.h
#import <Cocoa/Cocoa.h>
#interface WindowController : NSWindowController
#property (assign) IBOutlet NSTextField *internetLabel;
- (void)statutInternet;
- (IBAction)internet:(id)sender;
#end
WindowsController.m :
#import "WindowController.h"
#implementation WindowController
- (IBAction)internet:(id)sender;
{
[self statutInternet];
}
- (void)statutInternet;
{
NSLog(#"Callfunctionworks");
if (condition) {
[_internetLabel setStringValue:#"TxtFieldWorks!"];
}
}
I try to call the method statutInternet with this from another class :
WindowController *fenetre = [[WindowController alloc] init];
[fenetre statutInternet];
When I try to call a method from different class it doesn't work:
That's because, You're making another WindowController instance using this code:
WindowController *fenetre = [[WindowController alloc] init];
This is another new separate instance of same class, which I guess you're not showing. So you want to take reference to the window that's already showing rather than making a new instance.
Why Xcode want me to called my variables (like internetTextfield) with a _ before it ?
That's because when you declare variable using #property it does three things:
Makes an internal variable by adding the conventional underscore (_) to the start of the variable name. That's why you've _ as prefix of your variable.
Makes a setter-getter methods.
Takes the keywords you used (ie. assign, strong, weak) in account while implementing the setter-getters.
You can read a good discussion here: #property and retain, assign, copy, nonatomic in Objective-C
The NSTextField (and all of the other UI items) is not created yet when you call statutInternet method.
When your window loaded, your views will be ready :
_fenetre = [[WindowController alloc] initWithWindowNibName:#"WindowController"];
[_fenetre showWindow:_fenetre.window];
[_fenetre statutInternet];

NSObject call to self gives XCode error

I have a class WebServices that inherits from NSObject. I am using xcode4.2 and ARC turned on.
When I created the class, there was no other method in the NSObject lie viewDidLoad or init.
The issues is that when I try to call self.something or [self someMethod] Xcode flags my code red and complains with:
implicit conversion of Objective-C pointer type 'Class' to C pointer type 'struct obj_class*' requires a bridge cast
Please help. Why isn't cocoa like java where you call "this" and get the object you are in?
// WebService.h file
#interface WebService : NSObject
#property (weak, nonatomic) NSString * myString;
+(void) setAndPrintMyString:(NSString*) someString;
#end
//WebService.m file
#import "WebService.h"
#implementation WebService
#synthesize myString=_myString;
+(void) printMyString:(NSString*) someString{
[self setMyString:someString]; //XCode does not allow
NSLog(#"myString is set to %#",self.myString); //XCode dose not allow
}
#end
Declaring a method with + means that it is a class method. Within a class method self refers to the class itself, which in your case would be [WebService class]. If you declared and instance method (using -) then inside the method self would refer to the instance, which is what you want.
To set an instance variable - you need an instance
WebService *webService = [[WebService alloc] init];
webService.myString = #"some string";
Now to make your method work you need to declare it with a - instead of + which makes it an instance method
- (void)printMyString:(NSString *)someString
{
[self setMyString:someString];
NSLog(#"myString is set to %#",self.myString);
}
Now
[webService printMyString:#"boom"];
results in the instance variable myString being set to boom and the console logging out `myString is set to boom".
viewDidLoad method doesn't fit with NSObject subclass. It's a method for UI which will be there in UIViewController subclasses.
Now, Coming to point about self.something OR [self someMethod], That works perfectly well with NSObject subclasses. You need to show us the code, in which you are facing problem.
Just for your reference (I think you should start developing for iOS after going through this):
NSObject Class Reference
UIViewController Class Reference

How to pass values between 2 View Controllers without protocol?

I have two view controllers, call them viewA and ViewB
All the action happens in main view - ViewA
A menu button is hit, brings up ViewB, all is well and the menu comes up
Now, the user touches one IBAction button, which programmatically just needs to:
change the value of a BOOL, call it myBOOL to YES
dismiss ViewB
pass the myBOOL variables current state of YES back to ViewA
I have declared the same BOOL, set property, synthesized on both Views, but per my NSLog upon dismissal of ViewB and loading back up ViewA, it reverts back to NO
So I know I'm going off on a tangent, I just want to know if you can send the value of a BOOL between two controllers and if so, please show me an example... as searches have found Protocols and Delegate examples with NSString's, and when I attempt with a BOOL I get stuck in an import loop, however I've read that its possible to make a global BOOL, as bad design as it is, I just need to get over this block for now.
A question on this topic should really be focused more on NSNotificationCenter rather than NSUserDefaults, taking note that both are singletons.
NSUserDefaults:
The purpose of this class is NOT to pass variables between classes. It's purpose is, well, to store user's defaults. (ie preferences, settings, ... etc).
NSNotificationCenter:
This class is very handy, and has many different uses, one of which is to broadcast a variable for any class to receive. The receiving class is called the observer. This pattern is known as the Observer Pattern.
NOTE: The NSUserDefaults approach has the advantage of allowing you to set the variable before the other class is initialized, and can be retrieved at anytime. However, that's really sloppy (IMHO) and considered bad practice.
Quick and Dirty code sample on NSNotificationCenter:
// upon initializing the class that wants to observe the changes, we add it as an observer.
// So, somewhere in the A.m, upon being initialized (init, maybe?).
- (id)init {
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(calledUponNotif:)
name:#"MyObserveKey"
object:nil];
}
return self;
}
// the selector should look something like this:
- (void)calledUponNotif:(NSNotification *)notif {
id sentVar = [notif object];
}
// Somewhere in the B.m
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyObserveKey"
object:varToSend];
Another note: After calling the postNotification method, the registered selector in the other class will be called synchronously, so you don't have to worry about that.
This is not a good encapsulation answer but without being able to use protocols or delegates I don't believe it will have good encapsulation.
You can also create a global variable that you can set in one view controller and access in another.
ViewControllerOne.h
extern NSString *globalVariable;
#interface ViewControllerOne
#end
ViewControllerOne.m
#import "ViewControllerOne.h"
#implementation ViewControllerOne
NSString *globalVariables = #"Some String in the variable to access in second controller";
#end
ViewControllerTwo.m
#import "ViewControllerTwo.h"
#import "ViewControllerOne.h"
#implemetation ViewControllerTwo
- (void)viewDidLoad
{
NSLog("%#", globalVariables);
}
#end
This will print out into the console
****CONSOLE****
Some String in the variable to access in second controller
There is View-independent value keeping tool. You can use:
[[NSUserDefaults standardUserDefaults]setObject:<#(id)#> forKey:<#(NSString *)#>]
For example, you inputs strings or datas in A view, you can store them in above variables. And then, in B view, you can use them by below code:
[[NSUserDefaults standardUserDefaults]objectOrKey:<#(NSString *)#>]
These are a example of NSUserDefaults data using:
View A:
- (void)textFieldDidEndEditing:(UITextField *)sender
{
if (sender == homepage) {
[[NSUserDefaults standardUserDefaults]
setURL:[NSURL URLWithString:homepage.text] forKey:Ever5secHomepagePrefKey];
if( [homepage canResignFirstResponder] ) {
[homepage resignFirstResponder];
}
} else if (sender == userId) {
[[NSUserDefaults standardUserDefaults]
setObject:userId.text forKey:Ever5secUserIdPrefKey];
objectForKey:Ever5secUserIdPrefKey]);
if( [userId canResignFirstResponder] ) {
[userId resignFirstResponder];
}
} else if (sender == password) {
[[NSUserDefaults standardUserDefaults]
setObject:password.text forKey:Ever5secPasswordPrefKey];
if( [password canResignFirstResponder] ) {
[password resignFirstResponder];
}
}
}
View B:
userId.text = [[NSUserDefaults standardUserDefaults]
objectForKey:Ever5secUserIdPrefKey];
password.text = [[NSUserDefaults standardUserDefaults]
objectForKey:Ever5secPasswordPrefKey];
homepage.text = [[[NSUserDefaults standardUserDefaults]
URLForKey:Ever5secHomepagePrefKey]
description];
You don't need to use NSNotificationCenter, NSUserDefaults or global variables.
As long as the view controllers are related (and looking at the OP's question, they certainly seem to be) you can simply set the view controllers up to hold a reference to each another (with one of the references being weak of course in order to avoid a "retain", or "strong reference", cycle). Then each view controller can set the property on the other view controller as needed. Example follows...
NB: This concept is valid for any two related view controllers. However, the following code assumes that:
The view controllers in question are related via a navigation controller and the second view controller is attached to the first via a push segue.
iOS 5.0 or above is in use (as it makes use of storyboards).
FirstViewController.h
#interface FirstViewController : UIViewController
/* Hold the boolean value (or whatever value should be
set by the second view controller) in a publicly
visible property */
#property (nonatomic, assign) BOOL someBooleanValue;
/* Provide a method for the second view controller to
request the first view controller to dismiss it */
- (void)dismissSecondViewController;
#end
FirstViewController.m
#import "FirstViewController.h"
#import "SecondViewController.h"
#implementation FirstViewController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
/* Get the reference to the second view controller and set
the appropriate property so that the secondViewController
now has a way of talking to the firstViewController */
SecondViewController *vc = [segue destinationViewController];
vc.firstViewController = self;
}
- (void)dismissSecondViewController
{
// Hide the secondViewController and print out the boolean value
[self.navigationController popViewControllerAnimated:YES];
NSLog(#"The value of self.someBooleanValue is %s", self.someBooleanValue ? "YES" : "NO");
}
#end
SecondViewController.h
#import "FirstViewController.h"
#interface SecondViewController : UIViewController
// Create a 'weak' property to hold a reference to the firstViewController
#property (nonatomic, weak) FirstViewController *firstViewController;
#end
SecondViewController.m
#implementation SecondViewController
/* When required (in this case, when a button is pressed),
set the property in the first view controller and ask the
firstViewController to dismiss the secondViewController */
- (IBAction)buttonPressed:(id)sender {
self.firstViewController.someBooleanValue = YES;
[self.firstViewController dismissSecondViewController];
}
#end
Of course, the most correct way to handle this sort of inter-viewController communication is to use protocols/delegates/data sources so that the SecondViewController doesn't need to know the specifics of its parent/owner object. However, sometimes it is quicker/simpler to build a solution like this just to prove the concept. Then if all is well and the code is worth keeping, refactor to use protocol(s).
In the case where view controllers don't - and shouldn't - know about each other, it may be necessary to use NSNotificationCenter. Don't use global variables or NSUserDefaults for communication between view controllers.
There are two options available storing and retrieving data in different view controllers.
1)NSUserDefaults is best option for storing data and accessing in any other view controllers.
The NSUserDefaults class provides convenience methods for accessing common types such as float, double, integer, Boolean.
A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.
This is very easy and best method for storing and retrieving data.
if you want to read about NSUserDefaults, here I am sharing document.
NsuserDefaults Document.
2) You would create properties when you want them to be accessible outside the class or other view controllers.
Create property in this way. #property (nonatomic, retain) NSArray *arrayData; and then you can use this array value in other view controllers also.
Properties replace the accessor methods for objects.
You can see my answer here. Pass value from one view controller to another
There are two options available storing and retrieving data in different view controllers.
1)NSUserDefaults is best option for storing data and accessing in any other view controllers.
The NSUserDefaults class provides convenience methods for accessing common types such as float, double, integer, Boolean.
A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.
This is very easy and best method for storing and retrieving data.
if you want to read about NSUserDefaults, here I am sharing document.
[NsuserDefaults Document.][1]
2) You would create properties when you want them to be accessible outside the class or other view controllers.
Create property in this way. #property (nonatomic, retain) NSArray *arrayData; and then you can use this array value in other view controllers also.
Properties replace the accessor methods for objects.
I think best way to use powerful features of blocks in below ways.
In ViewB.h
typedef void (^CompletionHandler)(BOOL myBool);
#interface ViewB : UIViewController {
CompletionHandler completionHandler;
}
- (void)dismissHandler:(CompletionHandler)handler;
In ViewB.m
- (void)dismissHandler:(CompletionHandler)handler {
completionHandler = handler;
}
- (IBAction)dismiss:(id)sender {
completionHandler (YES); // your yes no logic here
}
In ViewA.m
- (IBAction)showPopup:(id)sender {
ViewB *vc = [[ViewB alloc] init];
[self.view addSubview:vc.view];
[vc dismissHandler:^(BOOL myBool) {
if (myBool) {
//Do your work;
}
}];
}

share NSArray between different UIViewControllers

I am making an application that uses a webService to get data in a JSON format... I get the data I parse them into a object NSArray ... and i use it .. it works fine ...
Now, if the user clicks a button I need to send him to an other Uiview ... which contains more data about the clicked object ..
The problem is here ... I don't want to request again and download the result from the server ... because i already did ... All I want is to have access to that NSArray that I have in the first UIViewController.
You can add on AnotherView.h another property:
#property (nonatomic, retain) NSArray *jsonData;
On AnotherView.m synthesize it. When you are going to to call AnotherView from InitialView, you can set jsonData with the data you retrieved on InitialView.
Create a custom initializer in your other view controller like so:
#import <UIKit/UIKit.h>
#interface OtherViewController : UIViewController
#property (nonatomic, strong) NSArray *myArray;
- (id)initWithArray:(NSArray *)anArray;
#end
Then implement it like so:
#import "OtherViewController.h"
#implementation OtherViewController
#synthesize myArray=_myArray;
- (id)initWithArray:(NSArray *)anArray {
if (!(self = [self initWithNibName:#"OtherViewController" bundle:nil]))
return nil;
if (!anArray) {
#throw [NSException exceptionWithName:#"OtherViewControllerBadInitCall" reason:#"array is nil" userInfo:nil];
}
_myArray = anArray;
return self;
}
//...
#end
You can then init and display your controller like so:
OtherViewController *otherViewController = [[OtherViewController alloc] initWithArray:greatJSONArray];
[self.navigationController pushViewController:otherViewController animated:YES];
There you go.
You can set the array as the property. You can either create a new class and set the array as the property and after you fetch the array, set the property. Or, you can create a property of the existing UIVIewController Class and pass the object.
Either way, you have to set property.
You could define a new property in your second ViewController that holds an NSArray and pass the firt array to the second ViewController before show it.
Well you have not outlined whether you send the data forward or backward. In the later case you will need to implement protocol and delegate(Define your own protocol) but for the prior case you just need to create the property of the Object you want to access in any other class. In case of web-services it is better to use protocol and delegates if u abide by the norms of MVC architecture.

Referencing UIViewController in NSObject

I am trying to access UIViewController's view in a NSObject class. The NSObject class is suppose to programmatically change UIViewController's user interface. I tried accessing NSViewController's view in NSObject by using self.view but was told that "request for member 'view' in something not a structure or union.
After a few inquiries, I was told that I need to reference viewController in my NSObject class. I am unsure of how to do this and would appreciate any help or point in the right direction. Thanks!
You should give the NSObject class a property called myController or something in its interface declaration, or pass a ViewController* to any methods that need to access it.
For the property, you can say:
ViewController* myController;
in the NSObject sub-class interface declaration, or for the method way, add an argument to your NSObject sub-class' method:
- (void) someMethodThatTakesAViewController: (ViewController*) theViewController {
//Do your stuff here
theViewController.view = [[UIView alloc] init]; // Or whatever you want to do
}
Hope this was what you were looking for.