How can I use an IBOutlet from another Class file into a different .m file? - iphone

MyGems.h
#import <UIKit/UIKit.h>
#interface MyGems : UIViewController {
IBOutlet UIImageView *lock1;
Collect.m
#import "Collect.h"
#import "MyGems.h"
#implementation Collect
- (IBAction) unlock {
if (lock1.hidden = NO) {
ruby.hidden = NO;
}
I get 4 errors, can someone please help? It seems to simple, but is confusing me.

You should almost never do this. The view controller (MyGems, which should be named something like MyGemsViewController) is responsible for managing this view. Other classes should not reach in and modify its IBOutlets. Doing so will cause you significant problems when the view managed by MyGems unloads and lock1 surprisingly becomes nil.
It's unclear what Collect is in this case; I assume it is another view controller? I'm not clear why unlock isn't a method on MyGemsViewController.
Also note that this code is incorrect, and should be throwing warnings at you telling you it's incorrect:
if (lock1.hidden = NO) {
This assigns NO to lock1.hidden. You meant to use ==, but you should never test against NO this way. You should do it this way:
if (! lock1.hidden) {
You must be careful of testing booleans against YES and NO. There are many true values that do not equal YES.

You need to make sure you have a reference to lock1 in your Collect class, most likely you will want to achieve this by having a reference to the Collect class in MyGems and then moving this view logic back into MyGems rather than in your Collect model class

Related

iPhone : pushing on UIViewController from where it pushed

I've two UIViewControllers say ViewController_A and ViewController_B
My flow is, AViewController_A ---> BViewController_B ---> CViewController_A
For A, B and C navigations I have the following situation,
A - In ViewController_A I've 100 of records which are in UITableViewCell, user select any one, and it will pushed to ViewController_B which will showing that selected record from ViewController_A.
B - Will show selected data from ViewController_A, and having Back button to go back (I can pop). Another thing, have a UIButton, If user tap this, it will again showing ViewController_A but with only that single record
C - Either we'll pop from ViewController_B then there's no issue, but I again want to push, so I need to #import ViewController_A in ViewController_B (as I've already imported ViewController_B in ViewController_A so I can't reimport ViewController_A in ViewController_B, right?) will create collision for those UIViewControllers.
What should be the better way to solve the problem in C, one suggestion is to make another `ViewController_D like and show the same as in ViewController_A but I think its not proper way, as I've already UI and coded for the functionality.
Suggestion needed. Let me know if you've any doubt!
What I understand is you have trouble with #import directive ?
I guess you are importing header in the .h file ? If so, do an #import in the .m file; and in the .h you should use #class YouViewControllerA.
Ex with AViewController:
.h
#class BViewController
#interface AViewController : UIViewController
{
//Your attributes here
}
#end
.m
#import "BViewController.h"
#implementation AViewController
//Some AViewController methods here
#end
Do this for both AViewController and BViewController and it should work.
EDIT : #import directives are designed to avoid recursive error, so anyway you shouldn't get error.
If you know a bit of preprocessing, the #import do the following (automatically) :
#ifndef TOTO_HEADER
#define TOTO_HEADER
//your methods here
#endif
More explanation :
When you write #import "toto.h" , at compile time the compiler will do the following check :
Is the preprocessoring variable "TOTO_HEADER" defined ? (this corresponding to the line ifndef "TOTO_HEADER")
If it isn't we define it (this is the #define "TOTO_HEADER" line) AND we "include" the methods and functions.
If it has been already defined, no need to include the methods (since they already get included).
This way we prevent header file from being included if they were already included. (and by extension it should not make any recursive error).
In other words : #import ensures that a header file is only included once so that you never have a problem with recursive includes.
one solution could be this-
First save the selected value in NSUserDefaults, and then take a bool when you click on the UIButton in viewController B.
Then push a segue to ViewController A and check the value of Bool there.
If its true then only print the value of NSUserDefaults else print the whole set of 100 records.

Why is my instance variable nil even though I'm setting it?

I've got a custom class BoardMatchData, containing information about a chess match.
I've also got a custom UIViewController named BoardViewController, which is alloc'ed when a user selects a match from a list.
Here's a snippet from where I create the BoardViewController, set its delegate, and then set the new BoardMatchData:
Games *selectedGame = [[self fetchedResultsController] objectAtIndexPath:indexPath];
if (!self.bvc) {
NSLog(#"Alloc-ing a BVC");
self.bvc = [[BoardViewController alloc] init];
self.bvc.delegate = self;
}
[self.bvc setNewBoardMatchData:[MasterViewController boardMatchDataFromGame:selectedGame]];
When debugging, I can see this method setNewBoardMatchData being called, and it has valid data coming into it. However, later on within the BoardViewController, this boardMatchData always seems to be nil. Here's the setNewBoardMatchData method:
- (void)setNewBoardMatchData:(BoardMatchData *)newBoardMatchData {
NSLog(#"BMD is being set");
if (self.boardMatchData != newBoardMatchData) {
self.boardMatchData = newBoardMatchData;
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
And within BoardViewController.h, I just have an instance variable:
BoardMatchData *boardMatchData;
A method declaration:
- (void)setNewBoardMatchData:(BoardMatchData *)newBoardMatchData;
And then at the top of BoardMatchData.m, I have:
#interface BoardViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
#property (nonatomic,retain) BoardMatchData *boardMatchData;
- (void)configureView;
#end
#synthesize boardMatchData = _boardMatchData;
...my intent here was to make sure that the setter was only ever being called by itself, and not by some other object.
Is there something wrong with how I'm trying to set self.boardMatchData?
I'm not doing any retains/releases because I'm using ARC.
Edit:
Caleb - I did that #synthesize statement as part of my flailing around trying to find this bug. Originally I had:
#synthesize boardMatchData;
... which I just switched back to. The behaviour is the same; self.boardMatchData always ends up nil, even after I've set it.
I think now I have only one ivar, boardMatchData, and I'm always accessing it through self.boardMatchData.
How do I prevent this from becoming nil?
#synthesize boardMatchData = _boardMatchData;
This says that the ivar the accessors should use is _boardMatchData, but you've also got an ivar named boardMatchData. If you're using the accessors, only _boardMatchData will be set. Since Objective-C automatically clears any ivars when your object is created, theboardMatchData ivar will always be nil.
Your comment (that you posted as an answer which you shouldn't do) suggests that you work on two different instances.
Here are a couple of possible reasons for this:
self.bvc is just assign property instead of retain.
You load one from within a nib and one is constructed in your code
(as shown) - this is probably the most like one. Maybe you just
forgot to wire up the outlet.
Your set self.bvc to nil somewhere so that you keep creating new
instances.
Aha; I found this question with an almost identical problem:
Objective-C – Retained property after being set is nil?
There was no conclusion to that one, but the last suggestion was to "try logging the address of self in -viewDidLoad and -viewWillAppear and -queueFinished. Something like NSLog(#"self is %p", self); and making sure they are the same."
I did that myself and now see that in initWithNibName/configureView/setNewBoardMatchData, I'm seeing one pointer for self, and then when viewDidLoad runs, I'm getting a different one!
I'm not sure how or why yet, but this clearly appears to be the problem (a new instance of my class is being instantiated AFTER I've set boardMatchData).
Edit:
While the above led me to the path of finding this bug (I was getting multiple versions of my BoardViewController), it's not the complete answer. I wanted to add here should anyone find this and be in the same position.
I was actually using storyboarding in Xcode4, and the Apple provided master-detail template with Core Data.
When I was instantiating my detail controller (BoardViewController), I was doing an alloc/init. I should have simply been referencing [segue destinationViewController], as the segue already instantiated a version for me.
I did not provide enough context to actually get to the root

What are alternatives to "delegates" for passing data between controllers?

Are there alternatives to "delegates" to pass back data from one controller to another?
Just seems like a lot of work implementing a delegate just to pass back the result from a child controller, back to the parent controller. Is there not another method? Are "blocks" one answer, and if so some example code would be great.
Delegates aren't a lot of work, aren't a lot of code, and are commonly the most appropriate solution. In my opinion they're neither difficult nor messy.
Five lines of code in the child's interface. Before #interface:
#protocol MyUsefulDelegate <NSObject>
- (void)infoReturned:(id)objectReturned;
#end
Inside #interface:
id <MyUsefulDelegate> muDelegate;
After #inteface's #end:
#property (assign) id <MyUsefulDelegate> muDelegate;
One line of code in the child's implementation:
[[self muDelegate] infoReturned:yourReturnObject];
One addition to an existing line of code in the parent's interface:
#interface YourParentViewController : UIViewController <MyUsefulDelegate>
Three lines of code in the parent's implementation. Somewhere before you call the child:
[childVC setMuDelegate:self];
Anywhere in the implementation:
- (void)infoReturned:(id)objectReturned {
// Do something with the returned value here
}
A total of nine lines of code, one of which is merely an addition to an existing line of code and one of which is a closing curly brace.
It's not as simple as a returning a value from a local method, say, but once you're used to the pattern it's super straightforward, and it has the power of allowing you do do all kinds of more complex stuff.
You could use many ways:
Calling a method of the super controller, needs casting maybe
Notifications
Simple Key-Value-Observing
Core Data
Example for for 1.
interface of your MainViewController: add a public method for the data to be passed
- (void)newDataArrivedWithString:(NSString *)aString;
MainViewController showing ChildController
- (void)showChildController
{
ChildController *childController = [[ChildController alloc] init];
childController.mainViewController = self;
[self presentModalViewController:childController animated:YES];
[childController release];
}
Child Controller header / interface: add a property for the mainViewController
#class MainViewController;
#interface ChildController : UIViewController {
MainViewController *mainViewController;
}
#property (nonatomic, retain) MainViewController *mainViewController;
Child Controller passing data to the MainViewController
- (void)passDataToMainViewController
{
NSString * someDataToPass = #"foo!";
[self.mainViewController newDataArrivedWithString:someDataToPass];
}
KVO or notifications are the way to go in many cases, but delegation gives a very good foundation to build upon. If you plan on extending the relationship between the view controllers in the future, consider using delegation.
Blocks are not really relevant to the above, but in short - it is a technique introduced with iOS 4, where you pass around blocks of code as variables/ parameters. It is very powerful and has many uses. For example, here is how you enumerate objects in an array using a block:
[someArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSLog(#"obj descriptions is - %#", [obj description]);
}];
The part from the ^ until the } is a block. Note that I've passed it in as parameter. Now, this block of code will be executed for every object in the array (i.e. output will be the description of each object).
Blocks are also very efficient performance-wise, and are used heavily in many new frameworks.
Apple's blocks beginners guide is quite good.
Check out NSNotificationCenter — NSNotificationCenter Class Reference
Folks pay a lot of attention the the V and the C in MVC, but often forget the M. If you've got a data model, you can pass it from one controller to the next. When one controller makes changes to the data stored in the model, all the other controllers that share the same model will automatically get the changes.
You might find using a singleton is practical. Just use it as a central storage for all your shared data.
Then throw in saving the state of your application too;)

How do I reference a pointer from a different class?

First off, I despise singletons with a passion. Though I should probably be trying to use one, I just don't want to. I want to create a data class (that is instantiated only once by a view controller on loading), and then using a different class, message the crap out of that data instance until it is brimming with so much data, it smiles.
So, how do I do that? I made a pointer to the instance of the data class when I instantiated it. I'm now over in a separate view controller, action occurs, and I want to update the initial data object. I think I need to reference that object by way of pointer, but I have no idea how to do that. Yes, I've set properties and getters and setters, which seem to work, but only in the initial view controller class.
There's no need to use a singleton if you don't like the pattern or if it doesn't fit. Assuming you are creating your second view controller in the first one, just declare an ivar and property for your model object in your second view controller and when you instantiate it, assign the model object to this property.
Make a global variable for your object and store it there on creation. You can wire that up in the init method (probably bad style), or from the caller or via interface builder. Just make your variable known in the files that use it.
Or - well - use some kind of singleton pattern and get the instance directly from that class. Looks much cleaner.
Seriously use a singleton. In case you don't like them cause you don't know the code:
#interface Order : NSObject {
NSMutableArray *order;
}
#property (nonatomic, retain) NSMutableArray *order;
+ (Order *)sharedInstance;
#end
#import "Order.h"
#implementation Order
#synthesize order;
+(Order *)sharedInstance {
static Order *myInstance = nil;
#synchronized(self) {
if(!myInstance) {
myInstance = [[Order alloc] init];
}
}
return myInstance;
}
-(void)dealloc {
[order release];
[super dealloc];
}
#end
Um. Hello. Isn't Core Data a good enough framework for you? It allows you to have a single persistent store and multiple contexts to manage updates and merging of changes in response to notifications. I may be out of line here, but seeing how you start the question with a strong opinion about a well accepted pattern in your first question indicates that you have not spent much time discovering the ways in which the objective c runtime and Foundation classes in iOS can collaborate to achieve a task. In any software, one object and only one object owns a specific resource. You should embrace singletons. I suggest you spend some time reading http://www.cocoadesignpatterns.com/. Oh yeah, check out the meaning of KVO.
Why not make it a property of your app delegate? That way you don't have to use the singleton pattern but you are taking advantage of Apple's already existing usage of the singleton pattern.
Don't forget that Objective-C is a superset of C.
Basically, a data class is a plain C struct.
If you want to access a variable of that class from another class, make it global.
mydata.h:
struct MyData {
// Whatever data fields that you need, e.g.:
NSInteger value;
};
extern struct MyData mydata;
mydata.c:
struct MyData mydata = {
// Whatever initial value that you find relevant, e.g.:
.value = 42;
};

Objective-C: Instance Method is Never Called

I've got a class called RootViewController with this:
Test *myLevelObject = [[Test alloc] init];
[myLevelObject afficher];
It is supposed to call my method -[ Test afficher] but there is no warning or error and it does enter the method. Any ideas? thx
Declaration of test class :
test.m :
#import "Test.h"
#import "RootViewController.h"
test.h :
#import <UIKit/UIKit.h>
#class RootViewController;
#interface Test : UIViewController <UIActionSheetDelegate,UITextFieldDelegate>{
-(void) afficher{
NSLog(#"hello");
poids.text = [NSString stringWithFormat:#"lol"];
labelalco.text = [NSString stringWithFormat:#"cac"];
}
it show in console "hello" but it doesnt execute my 2 line of code.
ok now i tryed to do UIAlertView and it works but it fails only when i use the variables
that are initialized in Test.h with interface builder.
Initialization :
IBOutlet UILabel *labelalco;
Interface builder :
alt text http://img72.imageshack.us/img72/4507/screenshot20100225at090.png
this is another screen shot of where the test and the 'drink' section which is rootviewcontroller is shown .
THANKS
It's possible that your init method is passing back nil, instead of an object. If this is the case, than the -afficher method is sent to nil, which doesn't break, but doesn't DO anything. Check your return value after the alloc/init.
Since you say an NSLog message within the afficher method is written to the console, your method is empirically being called. That is doesn't do what you intend it to do is a bug in your code. From the minimal listing you've provided, I'd guess that poids and lableco are not initialized properly (meaning they're still nil when you method is called).
If you're using IBOutlet in -init method, it won't work there (I guess it's set to nil), but it should work in -awakeFromNib method.
I echo what was stated above. Your afficher is likely getting invoked if you see the "hello" output in your console. (To be absolutely certain, clear your console prior to running and check that it shows again to rule out stale output.) One other thing I would suggest is to use an NSAssert() to be sure your outlets are set. Here's an example:
-(void) afficher{
NSLog(#"hello");
NSAssert(nil!=poids, #"poids should be set.");
poids.text = [NSString stringWithFormat:#"lol"];
NSAssert(nil!= labelalco, #"labelalco should be set.");
labelalco.text = [NSString stringWithFormat:#"cac"];
}
I just looked very closely at your screen shots and it appears that your Test object is a UIViewController. From the things you describe I bet you're assuming the labelalco to be set via interface builder connections. That will not happen in your 1st code snippet because you instantiate it directly without using the nib file.
Test *myLevelObject = [[Test alloc] init];
[myLevelObject afficher];
should be:
Test *myLevelObject = [[Test alloc] initWithNibName:#"Test.xib" bundle:nil];
[myLevelObject afficher];
Assuming Test.xib is where the connections are made in IB. It looks like this is a sub controller in your main window's tab view controller so there may be additional complexity in what you're trying to do.
Your latest comments say you're using a UITabBarController that contains RootViewController and Test.
But in RootViewController, you're trying to call a method in Test by creating a new instance of it. This instance would be separate from the one the tabbarcontroller is using.
I think in RootViewController, you need to instead obtain a reference to the same instance of Test the tabbarcontroller is using.
See this post for a possible way to do this:
iPhone SDK: How to access a ViewController nested in a TabBar from MyAppDelegate?
However, why do you have code in RootViewController that updates Test? If Test is displayed, could it not update itself instead? How does the code in RootViewController get executed after Test has already been displayed?