Why is xcode giving me a "method not found" error? - iphone

I have an object called Shot which is a subclass of UIIMageView.
// Shot.h
#import <Foundation/Foundation.h>
#interface Shot : UIImageView {
CGPoint position;
}
- (void)SetShot:(CGPoint *)point;
#end
// Shot.m
#import "Shot.h"
#implementation Shot
- (void)SetShot:(CGPoint *)point;
{
position.x = point->x;
position.y = point->y;
}
#end
When I try to call the SetShot method, xcode gives me this warning:
method -SetShot not found(return type defaults to id)
Here is the call:
//CustomImageView.m
#import "CustomImageView.h"
#class Shot;
#implementation CustomImageView
-(id) initWithCoder:(NSCoder *)aDecoder
{
self.userInteractionEnabled = YES;
return self;
}
-(void) setInteraction
{
self.userInteractionEnabled = YES;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
Shot *shot;
[shot SetShot:point];
}
- (void)dealloc
{
[super dealloc];
}
#end
When I run the program, I get a fatal error when the method is called. Why is that?

There are three problems in your code. Firstly, you need to import Shot.h in your CustomImageView.m implementation file:
#import "Shot.h"
instead of simply forward-declaring the Shot class:
#class Shot;
When the compiler sees a forward declaration, it becomes aware of the existence of that class but doesn’t yet know its attributes, declared properties, or methods — in particular, it doesn’t know that Shot has a -SetPoint: instance method.
Secondly, you’re not creating an instance of Shot:
Shot *shot;
[shot SetShot:point];
This only declares that shot is a pointer to Shot but there’s no allocation/initialisation. You should create an object, i.e.:
Shot *shot = [[Shot alloc] init];
then use it:
[shot SetShot:point];
and, when you don’t need it any longer, release it:
[shot release];
Although it’s not clear what’s the benefit of creating a shot, setting its point, and then releasing it. Unless your code is a contrived example, you might want to rethink this behaviour.
Also, your -SetPoint: method has a pointer to CGPoint parameter but you’re passing a CGPoint value (i.e., not a pointer) argument:
// point is not a pointer!
CGPoint point = [touch locationInView:self];
Shot *shot;
[shot SetShot:point];
I suggest you drop the pointer altogether, i.e.:
- (void)SetShot:(CGPoint)point;
{
position = point;
}
and maybe use a declared property instead of a manually implemented setter method.

Do
#import "Shot.h"
instead of:
#class Shot;

You have not created an instance of Shot; you have only created a pointer with:
Shot *shot;
You must allocate and initialize it:
Shot *shot = [[Shot alloc] init];
At some point you must import the header file Shot.h as well.

Related

What is wrong with this attempt to create and populate an NSMutableArray?

In my .h file, I have:
#property (nonatomic) NSMutableArray *cards;
In my .m file, I have, in the initializer:
- (id) init
{
self = [super init];
self.cards = [NSMutableArray alloc];
return self;
}
In a loop that populates a number of items visible and on-screen:
[self.cards addObject:noteView];
And in a touch event handler, I have:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"In touchesBegan.");
UITouch *touch = [touches anyObject];
UIView *selectedView = nil;
CGPoint touchLocation = [touch locationInView:self.view];
for (UIView *card in _cards)
{
CGRect cardRect = [card frame];
NSLog(#"%f", cardRect.origin.x);
NSLog(#"%f", cardRect.origin.y);
NSLog(#"%f", cardRect.size.height);
NSLog(#"%f", cardRect.size.width);
if (CGRectContainsPoint(cardRect, touchLocation)) {
NSLog(#"Match found.");
selectedView = card;
CGRect selectedFrame = selectedView.frame;
selectedFrame.origin.y = -selectedFrame.size.height;
selectedFrame.size = selectedView.frame.size;
float heightRatio = (float) floor([[UIScreen mainScreen] bounds].size.height + .4) / (float) selectedFrame.size.height;
float widthRatio = (float) floor([[UIScreen mainScreen] bounds].size.width + .4) / (float) selectedFrame.size.width;
float ratio = MIN(heightRatio, widthRatio);
selectedFrame.size.height *= ratio;
selectedFrame.size.width *= ratio;
selectedFrame.origin.x = -selectedFrame.origin.x * ratio;
}
}
}
The output for every touch I've made has been that the unconditional NSLog statement is output, but none of the "Log this float" statements execute. It seems that I have not correctly initialized or not correctly populated NSMutableArray.
I seem to get the same behavior whether I refer to _cards or self.cards.
Thanks for any help,
--EDIT--
I seem to be sticking on something else than originally thought of. I now have self.cards = [[NSMutableArray alloc] init], but identical behavior: I go through a loop and populate a bunch of cards, but when I tap one of them, the touch handler outputs "In touchesBegan." but none of the floats. Given an updated init, why would touchesBegan be acting as if it hadn't seen any cards after a number of cards are visible onscreen? (The other output should give a number of lines of floats whether or not the touch was on target for a particular card.)
You need to init the array!
self.cards = [[NSMutableArray alloc] init];
Or
self.cards = [NSMutableArray new];
They are both equivalents
With alloc the only thing you are doing is reserve space in the memory for that variable.
From Apple Documentation:
alloc
Returns a new instance of the receiving class.
init
Implemented by subclasses to initialize a new object (the receiver) immediately after memory for it has been allocated.
Are you using a storyboard for this? Try to initialize your cards property in viewDidLoad.
As a quick check, try to initialize that property right before the loop that is adding objects to it.

Why do CALayer's move slower than UIView's?

I have a UIView subclass that moved around 'event's when the user touches them, using the following overridden method:
// In a custom UIView...
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint point = [[touches anyObject] locationInView:self];
UIView *eventView = [self.ringOfEvents hitTest:point withEvent:event];
for (EventView *e in self.events) {
if (e == eventView) {
event.position = point;
}
}
}
Why is it that when I make EventView a CALayer instead of UIView, the movement slows down to a creep? I can post that code too, but it is so similar that I don't think it is necessary.
I would think that abstracting to a lower level would speed up event handling, but I must be missing something.
By the way, either if *eventView is a subclass of UIView or CALayer, the position property is as follows:
- (void)setPosition:(CGPoint)pos {
self.layer.position = pos;
}
- (CGPoint)position {
return self.layer.position;
}
Not sure why I get a huge decrease in latency when using UIView as apposed to CALayer..
Most CALayer properties are changed with animation by default, so decrease in latency is probably caused by that.
You may want to disable animations when layer position is changed. Possible solutions are discussed for example in here and here
This is due to implicit animations.
I've implemented a category method which removes implicit animation for givven keys and can be used like this
[view.layer setNullAsActionForKeys:#[#"bounds", #"position"]];
Implementation
#implementation CALayer (Extensions)
- (void)setNullAsActionForKeys:(NSArray *)keys
{
NSMutableDictionary *dict = [self.actions mutableCopy];
if(dict == nil)
{
dict = [NSMutableDictionary dictionaryWithCapacity:[keys count]];
}
for(NSString *key in keys)
{
[dict setObject:[NSNull null] forKey:key];
}
self.actions = dict;
}
#end

UIImageView scope. Accessing from another class

Here's part of the code I'm working with: http://pastie.org/2472364
I've figured out how to access the UIImageView from another method within the same class file in which it was programmatically created.
However, I was wondering how I'd access that same UIImageView from within the LetterTiles.m file, specifically within the touchesMoved method. The way I wrote the code in the sample, it will only show if the frames intersect if they're on top of each other when the otherMethod is called. Of course, I need to be able to check if the views intersect within the actual touchesMoved method. I'm sure it's something super easy, but I'm just not sure how to do it.
Thanks in advance for any help you can give me.
From your comment, and using the code you already have, I would go down this route. This isn't what I would do personally, just FYI. The structure is a bit shakey with the way it sounds like you want this.
Create the place holder UIImageView in the touchesBegan function, then check to see if they intersect when the user stops moving the image.
#import "LetterTiles.h"
#implementation LetterTiles
#synthesize placeHolder;
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
// Retrieve the touch point (I consider this useful info to have, so I left it in)
CGPoint pt = [[touches anyObject] locationInView:self];
startLocation = pt;
// Create a place holder image wherever you want
[self setPlaceHolder:[[[UIImageView alloc] initWithFrame:CGRectMake(39, 104, 70, 70)] autorelease]];
[newImage setImage[UIImage imageNamed:#"placeHolder.png"]] autorelease];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint pt = [[touches anyObject] locationInView:[self superview]];
[self setCenterPoint:pt];
}
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
LetterTiles *movingTile = self;
if (CGRectIntersectsRect([movingTile frame], [placeHolder frame])) {
NSLog(#"Touched");
[self setFrame:[placeHolder frame]];
}
}
Make a protocol called ViewMoved which will contain one method otherMethod.
implement that in myMainViewController
take a delegate property of type ViewMoved in LetterTiles.
Assign self when you make new object of type LetterTiles in myMainViewController.
On every movement of touch call oherMethod of delegate and check whether any views of type LetterTiles are intersecting or not.
This will catch any intersection when any of the view is moved.....
If above is not matching with your question then write here......

(UIView *)hitTest problem?

I have one Masterview.it has lot of childviews.I am using the following code to detect the touched view and to bring front the corresponding view.the code works fine.But When I add subview to childview,it did not work , any help please?
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
{
self.hitView = nil;
self.hitView = [super hitTest:point withEvent:event];
int x = self.hitView.frame.origin.x;
int y = self.hitView.frame.origin.y;
NSLog(#"x = %d",x);
NSLog(#"y = %d",y);
if ([self.viewDelegate respondsToSelector:
#selector(view:hitTest:withEvent:hitView:)])
{
return [self.viewDelegate view:self hitTest:point
withEvent:event hitView:hitView];
}
else
{
[self bringSubviewToFront:self.hitView];
return hitView;
}
}
If I get it right, it's pretty easy: hitTest always returns the farthest descendant subview in the view. If there is no subview this is always the same view. If there is one, that subview might be returned instead. Here's how you could fix it:
self.hitView = [super hitTest:point withEvent:event];
if ([self.hitView isDescendantOfView: self])
self.hitView = self;
EDIT Now that I understand the problem better, you should maybe do the following. This code returns the superview that's a direct descendant of the outer view:
UIView *hitView = [super hitTest:point withEvent:event];
while (hitView && hitView.superview != self)
hitView = hitView.superview;
(Please also note that you should use a local variable and change your property later than).
As you said you're moving UIViews around. You should check out the video from WWDC 2010 where they achieve this effect with the new GestureRecognizer Class.
The video you should look for is named somthing with "Gesture Recognition". If you don't find it I can link it when I'm home from work.
I wish you the best of luck!

iphone: protocol works in simulator, but not on device - i dont know any further

i wanted to implement an optional protocol for my subviews.
the subview-class is inherited by uiviewcontroller and the most viewcontrollers are inherited by this subview. i think it is a simple structure.
the problem is: it only works in the simulator. on a device just the first nslog appears, then the application closes. on the simulator it works fine.
what could it be ?
sure you see, that some things are commented out, but they dont have any effort.
the protocol:
#protocol CustomGestures <NSObject>
#optional
-(void)nextPage;
-(void)previousPage;
#end
parts of my subviewcontroller
#implementation SubViewController
//handles gestures and passes them
//further to the superclass
//uiviwcontroller to take the normal behaviour
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
//pass the information to superclass
[super touchesEnded:touches withEvent:event];
//create object with protocol and some other objects
id<CustomGestures> gestures;
UITouch* touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
CGFloat deltaX = gestureStartPoint.x - currentPosition.x;
CGFloat deltaY = fabs(gestureStartPoint.y - currentPosition.y);
//if the subclass (self) has the protocol implemented, use the protocoll-features
if ([self conformsToProtocol:#protocol(CustomGestures)])
{
if (deltaX <= -80 && deltaY <= 20) {
NSLog(#"vorige seite");
[gestures nextPage];
} else
if (deltaX >= 80 && deltaY <= 20) {
[gestures previousPage];
NSLog(#"nächste seite");
}
}
}
}
parts of a subview
#interface NavInfos :
SubViewController <CustomGestures>{
....}
#implementation NavInfos
//CustomGestures-Protocol
-(void)nextPage{
NSLog(#"nächste seite im navinfos");
//[[TTNavigator navigator] openURLAction:[TTURLAction actionWithURLPath:#"tt://mapkit"]];//applyAnimated:YES]
}
-(void)previousPage{
NSLog(#"vorige seite im navinfos");
//[[TTNavigator navigator] openURLAction:[TTURLAction actionWithURLPath:#"tt://windows"]];
//applyTransition:UIViewAnimationTransitionFlipFromLeft] applyAnimated:YES]
}
First of all, since you're declaring those two methods as #optional a check to conformsToProtocol: is not enough to verify that they respond to the methods in question. You should either remove the #optional keyword or add a check to respondsToSelector: as well as the conformsToProtocol: deal.
As for your problem, you are not setting gestures to anything, but you're calling methods in it anyway. You want to set it to something (e.g. self) or switch the two calls from [gestures ...] to [self ....].