Call a method from another class when shake is detected - iphone

HERE IS THE CODE: http://min.us/mWdMO0n14
I'm a Obj C newbie, so I've searched quite a bit, but haven't found anything that can solve my problem.
I have CalculatorViewController.h and .m and then CalculatorBrain.h and.m (Stanford Lectures)
in CalculatorBrain.m, I have the following method, with all of the variables defined as private in the CalculatorBrain header.
- (void)clearEverythingOnShakeGesture{
operand = 0;
waitingOperation = #"";
waitingOperand = 0;
}
Then in CalculatorBrain.m , I have everything set up to detect shakes, as follows. I've included some of the code above the shake detection just so you have a general idea.
#interface CalculatorViewController()
#property(nonatomic, retain) CalculatorBrain *brain;
#end
#implementation CalculatorViewController
#synthesize brain;
- (CalculatorBrain *)brain {
if (!brain) {
brain = [[CalculatorBrain alloc] init];
}
return brain;
}
-(BOOL)canBecomeFirstResponder{
return YES;
}
-(void)viewDidAppear: (BOOL) animated{
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.subtype == UIEventSubtypeMotionShake)
{
NSLog(#"SHAKE IT!");
[brain clearEverythingOnShakeGesture]; //********** not sure how to call this.
}
}
I'm not sure how to call [brain clearEverythingOnShakeGesture]; , because I get the error "Class method +clearEverythingOnShakeGesture not found, defaults to return type id". However, if I make it a class method, the variables inside are instance variables, which provides another error. Any help greatly appreciated.

The project's AppDelegate posted in the comment above is building the calculator view controller from a nib, then releasing it immediately. The app functions partially, but the UILabel property to be cleared on the shake gesture is nulled at that point.
Also, it's a good practice to declare private properties in the private category, synthesize them with _underscore aliases, and refer to them as self.property outside of synthesized methods.

Are you #import-ing the CalculatorBrain.h? Also, you're using a nice lazy initialization pattern by building the CalculatorBrain in the getter, but you're not calling the getter in the motionBegan: method. Try [self.brain clearEverything ...] to get the brain instance.
I don't see anything in the code that would make the compiler think you have a class method. So that's mysterious. Please double check about the header import. You are correct that the clearEverything... should be an instance method.

Related

Override method using runtime library

I need to override - (void)viewWillAppear:(BOOL)animatedfor all ViewControllers adding NSLog(#"blabla") in this method. I.e. after every call of viewWillAppear invokes implemented early realization of viewWillAppear + my NSLog message. Is it possible? If yes, please give me an advice.
Currently I have tried this code
#implementation RuntimeTest
IMP previusImp;
IMP newIMP;
- (void)ovverrideViewWillAppearInViewController:(Class)vcClass {
newIMP = class_getMethodImplementation([self class], #selector(viewWillAppear:));
Method viewWillAppearMethod = class_getInstanceMethod(vcClass, #selector(viewWillAppear:));
previusImp = method_setImplementation(viewWillAppearMethod, newIMP);
}
- (void)viewWillAppear:(BOOL)animated {
previusImp(self, #selector(viewWillAppear:), animated);
NSLog(#"log2");
}
#end
then I have
#implementation IRViewController2
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"log");
}
#end
My custom viewWillAppear invokes first, then viewWillAppear from IRViewController2. And after this my application crashes with EXC_BAD_ACCESS. What's wrong?
if you can't use a subclass because you want to do it globally, but also don't want to lose the old version of the method (so category override is out of the question) then you need to swizzle away the old method, put in your new method that then calls the old method... I have used something called JRSwizzle to perform this in the past, it makes it easy to swizzle without writing a bunch of runtime code.
if (![self jr_swizzleMethod:#selector(originalInit) withMethod:#selector(init) error:&err])
{
NSLog(#"unable to jr_swizzle methods, error: %#",err);
exit(EXIT_FAILURE);
}
if (![self jr_swizzleMethod:#selector(init) withMethod:#selector(myInit) error:&err])
{
NSLog(#"unable to jr_swizzle methods, error: %#",err);
exit(EXIT_FAILURE);
}
then your -myInit method can call -originalInit or whatever methods you are using.

How to interact between classes

A very basic question on how to interact between classes here: how can I trigger an action called by clicking on a button linked to one class (the graphic user interface in my case - which does not contain any drawing code) inside another class (my drawing class - which is defined programmatically)?
Thanks!
Edited: I have tried to implement the solutions suggested below but I didn't manage to trigger the action from the other class. I have two classes: the main view controller and a class with the drawing code. Any advice would be highly appreciated. Thanks!
//MainViewController.m
//This class has a xib and contains the graphic user interface
- (void)ImageHasChanged
{
//do something on the GUI
}
//DrawView.m
//This class has no associated xib and contains the drawing code
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//I want to call ImageHasChanged from MainViewController.m here
//How can I do this?
}
Inter-class functionality is done simply by importing one class into the other, and calling an accessible method/instance variable on the import.
For the button IBAction example in your question:
ClassA.m (This will be imported via its header):
#import "ClassA.h"
#implementation ClassA
// This is a class-level function (indicated by the '+'). It can't contain
// any instance variables of ClassA though!
+(void)publicDrawingFunction:(NSString *)aVariable {
// Your method here...
}
// This is a instance-level function (indicated by the '-'). It can contain
// instance variables of ClassA, but it requires you to create an instance
// of ClassA in ClassB before you can use the function!
-(NSString *)privateDrawingFunction:(NSString *)aVariable {
// Your method here...
}
#end
ClassB.m (This is your UI class that will call the other method):
#import "ClassA.h" // <---- THE IMPORTANT HEADER IMPORT!
#implementation ClassB
// The IBAction for handling a button click
-(IBAction)clickDrawButton:(id)sender {
// Calling the class method is simple:
[ClassA publicDrawingFunction:#"string to pass to function"];
// Calling the instance method requires a class instance to be created first:
ClassA *instanceOfClassA = [[ClassA alloc]init];
NSString *result = [instanceOfClassA privateDrawingFunction:#"stringToPassAlong"];
// If you no longer require the ClassA instance in this scope, release it (if not using ARC)!
[instanceOfClassA release];
}
#end
Side note: If you're going to require ClassA a lot in ClassB, consider creating a class-wide instance of it in ClassB to re-use wherever it's required. Just don't forget to release it in dealloc (or maybe set it to nil in ARC) when you're finished with it!
Finally, please consider reading through the Apple Docs on Objective-C classes (and indeed all other sections of the documentation relevant to what you're trying to achieve). It is a bit time-consuming, but very well invested in the long run into building your confidence as an Objective-C programmer!
//As you said an instance of MainViewController has to be created first
MainViewController *instanceOfMainViewController = [[MainViewController alloc]init];
[instanceOfMainViewController ImageHasChanged];
//Thanks for your help Andeh!
Actually you can use #protocol(Delegate) to interact message between two classes this is standard way Or refer this document
http://developer.apple.com/library/ios/#documentation/General/Conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html to learn more

Inheritance in Objective C

I have a main game class which renders the game using Open GL. Now I thought I could inherit from this class and then just call [super init] in its init method to get a copy of it. The plan was to make some modifications in the copy but as it seems this doesn't work.
The following is the header file of my class:
#import "GameView.h"
#interface CloneView : GameView {
}
-(id)initWithFrame:(CGRect)frame;
#end
And this is the Clone view class:
#implementation CloneView
-(id)initWithFrame:(CGRect)frame{
return [super initWithFrame:frame];
}
#end
If I set a break point in the init method in the GameView class it stops there. Thing is: my clone view doesn't get rendered, the screen stays black.
What am I missing? Thanks for your help!
Edit
Just for the record: I tried without implementing initFrame and got the same result. (as expected as the initFrame as above isn't doing anything apart from calling super)
Edit 2
I'm adding my clone to another view so I'm creating two Eagle contexts. Could that be the reason why it doesn't work?
If you are not adding anything in the init function of CloneView than you don't even have to rewrite it. You can just have your class inherit from GameView and it automatically copies it's init function.
This is from the apple docs
You should assign self to the value returned by the initializer because the initializer could return an object different from the one returned by the original receiver.
So Try doing this
-(id)initWithFrame:(CGRect)frame{
if(self = [super initWithFrame:frame] ) {
//Do whatever you need to do here.
}
return self;
}
This should fix your issue if you need to do something in your init method. Otherwise you can skip the init method altogether.
try doing this it may work..
return(self=[super initWithFrame:frame])
which ensures the super class method is copied properly to the current method
TNQ
I finally located the problem:
I needed to write a second init method. The problem was that the following code was being executed twice:
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[super layer];
[eaglLayer setOpaque:YES];
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
printf("FAIL \n");
[self release];
return nil;
}
[Textures loadTexture];
if ([self createFramebuffer]) {
[self draw];
}
This code was in the initFrame method of the game class. I made a second init method that does not execute this code. Instead, this code is executed in the parent view class of the clone. Now it works, YAY!!
Thanks for trying to help me!

Problem with reinitializing of string in iPhone

I have an interface like this:
#interface MacCalculatorAppDelegate:NSObject
<UIApplicationDelegate> {
// ...
UIButton *operatorPressed;
NSString *waitingOperation;
}
And I am initializing waitingOperation variable in my implementation like this:
- (id)init {
if (self = [super init]) {
waitingOperation = #"not set";
}
return self;
}
And I want to reinitialize this variable in a function. This is calculator program and when user clicks on operators button the following function will be invoked:
- (IBAction)operatorPressed:(UIButton *)sender {
if([#"+" isEqual:operand]) {
waitingOperation = #"+";
}
}
But after the check in if statement, my program won't do anything and this happens when I am trying to reinitialize waitingOperation variable.
I am new to objective-c, please help me understand what's wrong here.
Thanks in advance.
There are several things to note here.
waitingOperation=#"not set";
As it stands, this will eventually crash your program. Objective C string literals are autoreleased instances of NSString, which means unless you assign it to a retained property or retain it manually, the memory will be deallocated, leaving a dangling pointer.
-(IBAction) operatorPressed:(UIButton *)sender {
Have you verified that this method is actually being called? You have to assign the IBAction in Interface Builder. Step through it in the debugger or use NSLog to verify that this method is being called.
if([#"+" isEqual:operand])
Where is operand coming from?
waitingOperation=#"+";
Same problem as above, this will get deallocated behind the scenes, leaving you with a dangling pointer.
Also, note that if you know that both variables are NSStrings, using isEqualToString: is faster than using isEqual:.
Finally, this stuff shouldn't be part of your app delegate. This is view controller logic.
If your operand is also a string, then check for isEqualToString instead of isEqual

Setting a delegate using blocks in iPhone

On a view controller I have multiple textfields, which all use the same delegate. Now in the delegate the code gets really ugly since I have to differentiate between all the textfields (bunch of if/else-if or a switch statement). I came a cross this article:
Blocks in textfield delegates
But from this I still don't understand how this solves the problem? Doesn't this basically call one method and pass it the text and the method has no idea what textfield gave the string? You would still need to differentiate between the textfields, but this time inside the block (with the usual if(textfield == bazTextField)...).
I don't know that it exactly solves the problem so much as shifts it (and into viewDidLoad, which usually gets a bit of mush-mash in it anyway).
However in that example the block itself was being passed in the textfield to run comparisons with and "remembers" the values of all the instance variables as well (if it refers to them), so that's how it knows what text and text field is being dealt with.
I don't see how that code exactly is supposed to help though, since it assigns one block to the single delegate class to be used with all text field delegates - unless perhaps you were supposed to have one per text field, each with a different block. Then you have way more code than you'd have had with the if statements!
The article doesn't make it clear, but I believe the idea is to create one of these blocks (and block delegate objects) for each UITextField that you wish to have respond to textFieldShouldReturn.
hm, maybe I didn't completely understand the article, but I don't see the advantage of using blocks instead of selectors in that concrete example.
you could achieve something similar like this
#interface AlternativeTextFieldDelegate : NSObject <UITextFieldDelegate>
{
SEL selectorToCall;
id objectToCall;
}
- (void) setObjectToCall:(id)obj selector:(SEL)selector;
#end
#implementation AlternativeTextFieldDelegate
- (void) setObjectToCall:(id)obj selector:(SEL)selector
{
objectToCall = obj;
selectorToCall = selector;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[objectToCall performSelector:selectorToCall];
return YES;
}
#end
and the view controller
#interface ViewWithTextFieldsController : UIViewController
{
UITextField *tf1;
AlternativeTextFieldDelegate *delegateForTF1;
UITextField *tf2;
AlternativeTextFieldDelegate *delegateForTF2;
}
// ...IBOutlets and all that...
- (void) tf1ShouldReturn; // handles shouldReturn for tf1
- (void) tf2ShouldReturn; // handles shouldReturn for tf2
#end
#implementation ViewWithTextFieldsController
- (void) viewDidLoad // or wherever
{
delegateForTF1 = [[AlternativeTextFieldDelegate alloc] init];
[delegateForTF1 setObjectToCall:self selector:#selector(tf1ShouldReturn)];
tf1.delegate = delegateForTF1;
delegateForTF2 = [[AlternativeTextFieldDelegate alloc] init];
[delegateForTF2 setObjectToCall:self selector:#selector(tf2ShouldReturn)];
tf2.delegate = delegateForTF2;
}
// ...
#end
don't really know if that's any better than chaining if-elses though.
it seems to me that this complicates things more than the problem it solves.