No visible #interface for 'NSObject<PageControlDelegate>' declares the selector 'pageControlPageDidChange:' - iphone

I've got this code from this post and everything is ok except one thing, I have this error that I dont know how to fix. this is the code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint touchPoint = [[[event touchesForView:self] anyObject] locationInView:self];
CGRect currentBounds = self.bounds;
CGFloat x = touchPoint.x - CGRectGetMidX(currentBounds);
if(x<0 && self.currentPage>=0){
self.currentPage--;
[self.delegate pageControlPageDidChange:self];
}
else if(x>0 && self.currentPage<self.numberOfPages-1){
self.currentPage++;
[self.delegate pageControlPageDidChange:self];
}
}
the error is that:
No visible #interface for 'NSObject<PageControlDelegate>' declares the selector 'pageControlPageDidChange:'.
I've tried to run this code with ARC, I removed the dealloc method, changed assign to weak in here:
#property (nonatomic, weak) NSObject<PageControlDelegate> *delegate;
as someone wrote in the answers, but still it isn't working.
Please help me.

If in your header you just have:
#protocol PageControlDelegate;
You missed the actual definition of what the protocol is. At the bottom of that first code block in your linked post there is this code that contains the declaration of the missing protocol function:
#protocol PageControlDelegate<NSObject>
#optional
- (void)pageControlPageDidChange:(PageControl *)pageControl;
#end

You need to import the .h file that declares the PageControlDelegate protocol.

Related

user grab and move an image in app iPhone

I am trying to have a user grab and move an image in an app I am building.
I have created a UIImageView like this
DragView.h
#import <UIKit/UIKit.h>
#interface DragView : UIImageView{
}
#end
and
DragView.m
#import "DragView.h"
#implementation DragView
-(void) touchesMoved:(NSSet *)set withEvent:(UIEvent *)event {
CGPoint p = [[set anyObject] locationInView:self.superview];
self.center = p;
}
#end
I then imported the .h file into my main View controller and assigned DragView to a UIImageView in the IB Identity Inspector.
I must be missing something because when I build and run and then touch the image it does't move. What and I missing?

Why doesn't Xcode recognize -ignoreTouch:forEvent: in UITapGestureRecognizer subclass?

According to documentation there is a method -ignoreTouch:forEvent:. I need to mimic a -pointInside:withEvent: functionality to make it handle taps only specific shape.
But when I try call this method Xcode doesn't know it. No autocompletion and it gives error for undeclared method. But documentation clearly says we can call ignoreTouch:forEvent: on UITapGestureRecognizer to ignore a touch.
Edit: Documentation says there is a class extension for it but I cant import this either. Not found: #import "UIGestureRecognizerSubclass.h"
It's part of UIKit so import it like this:
#import <UIKit/UIGestureRecognizerSubclass.h>
It seems when I do this it works:
#import <UIKit/UIGestureRecognizerSubclass.h>
#implementation OFShapeTapGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//[self ignoreTouch:[touches anyObject] forEvent:event];
// ... here, fancy logic.
if (ignore) {
self.state = UIGestureRecognizerStateCancelled;
}
}

How do I apply a UILongPressGestureRecognizer to a shape in C4?

I am working in C4 and want to be able to press down on a shape and drag it at a slow speed upward.
I have been working with the "Getting the UITouch objects for a UIGestureRecognizer" tutorial and other TouchesMoved and TouchesBegan Objective-C tutorials but the gesture is not applying to the shape. The project builds but the shape still does not move when you press and drag it. Is there a particular way C4 applies gestures to shapes?
This is my code:
DragGestureRecognizer.h
#import <UIKit/UIKit.h>
#interface DragGestureRecognizer : UILongPressGestureRecognizer {
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
#end
#protocol DragGestureRecognizerDelegate <UIGestureRecognizerDelegate>
- (void) gestureRecognizer:(UIGestureRecognizer *)gr movedWithTouches:(NSSet*)touches andEvent:(UIEvent *)event;
#end
C4WorkSpace.m:
#import "C4WorkSpace.h"
#import "DragGestureRecognizer.h"
#implementation DragGestureRecognizer
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
if ([self.delegate respondsToSelector:#selector(gestureRecognizer:movedWithTouches:andEvent:)]) {
[(id)self.delegate gestureRecognizer:self movedWithTouches:touches andEvent:event];
}
}
#end
#implementation C4WorkSpace {
C4Shape *circle;
}
- (void)longPress:(UILongPressGestureRecognizer*)gesture {
if ( gesture.state == UIGestureRecognizerStateEnded ) {
NSLog(#"Long Press");
}
}
-(void)setup {
circle = [C4Shape ellipse:CGRectMake(5,412,75,75)];
[self.canvas addShape:circle];
}
#end
C4 has a method that simplifies the process of adding gestures to any visible object (e.g. C4Shape, C4Movie, C4Label...).
NOTE: you will need to create a subclass of C4Shape in which you can create some custom methods.
For instance, the following will add a tap gesture to an C4Shape:
C4Shape *s = [C4Shape rect:CGRectMake(..)];
[s addGesture:TAP name:#"singleTapGesture" action:#"tap"];
The addGesture:name:action: method is listed in the C4Control documentation, and is defined like:
Adds a gesture to an object.
- (void)addGesture:(C4GestureType)type name:(NSString *)gestureName action:(NSString *)methodName
The (C4GestureType)type can be any one of:
TAP
SWIPERIGHT
SWIPELEFT
SWIPEUP
SWIPEDOWN
PAN
LONGPRESS
The following gestures are available, but haven't yet been tested:
PINCH
ROTATION
The (NSString *)gestureName allows you to specify a unique name for the gesture.
The (NSString *)methodName allows you to specify the method that the gesture will trigger.
So... getting back to the example above:
[s addGesture:TAP name:#"singleTapGesture" action:#"tap"];
...will add to the s object a TAP gesture called singleTapGesture which, when triggered, will run a -(void)tap; method (which needs to have already defined in your class).
You can apply the technique I described above, but instead use:
[s addGesture:PAN name:#"panGesture" action:#"move:];
you will also have to define a
-(void)move:(id)sender;
method in your shape's class.

UIControl Not Receiving Touches

I have a UIControl which implements the touches began method like so:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
//More code goes here
This subclass of UIControl is instantiated in a view controller, it is then added as a subview to that view controller. I have a breakpoint at the touches began method of the UIControl, and the method never gets called. I've been doing some reading and it seems that the View Controller has some logic that decides whether to pass on touch events to its subviews. The strange thing is that I have a different subclass of UIControl in the same view controller, and the touch events get passed down to it when the user touches it!
Here is the full code:
.h
#import <UIKit/UIKit.h>
#interface CustomSegment : UIView
#property (nonatomic, strong) UIImageView *bgImageView;
#property (nonatomic, assign) NSInteger segments;
#property (nonatomic, strong) NSArray *touchDownImages;
#property (nonatomic, readonly, assign) NSInteger selectedIndex;
#property (nonatomic, weak) id delegate;
- (id)initWithPoint:(CGPoint)point numberOfSegments:(NSInteger)_segments andTouchDownImages:(NSArray *)_touchDownImages;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
#end
.m
#import "CustomSegment.h"
#implementation CustomSegment
#synthesize bgImageView, segments, touchDownImages, selectedIndex, delegate;
- (id)initWithPoint:(CGPoint)point
numberOfSegments:(NSInteger)_segments
andTouchDownImages:(NSArray *)_touchDownImages
{
self = [super initWithFrame:CGRectMake(point.x, point.y, [[_touchDownImages objectAtIndex:0] size].width, [[touchDownImages objectAtIndex:0] size].height)];
if (self)
{
touchDownImages = _touchDownImages;
segments = _segments;
bgImageView = [[UIImageView alloc] initWithImage:[touchDownImages objectAtIndex:0]];
[self addSubview:bgImageView];
}
return self;
}
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
float widthOfSegment = [self frame].size.width / segments;
float bottomPoint = 0;
float topPoint = widthOfSegment;
for (int i = 0; i < segments; i++)
{
if ([touch locationInView:self].x > bottomPoint && [touch locationInView:self].x < topPoint)
{
[bgImageView setImage:[touchDownImages objectAtIndex:i]];
selectedIndex = i;
return;
}
else
{
bottomPoint = topPoint;
topPoint += topPoint;
}
}
}
#end
tl;dr Set all subviews of the UIControl to setUserInteractionEnabled:NO. UIImageViews have it set to NO by default.
Original Post
One thing I found recently is that it helps if the top-most subview of the UIControl has setUserInteractionEnabled:NO. I arrived at this because I had a UIControl subclass with a UIImageView as it's only subview and it worked fine. UIImageView has userInteractionEnabled set to NO by default.
I also had another UIControl with a UIView as it's top most subview (technically the same UIControl in a different state). I believe UIView defaults to userInteractionEnabled == YES, which precluded the events being handled by the UIControl. Settings the UIView's userInteractionEnabled to NO solved my issue.
I don't know if it's the same issue here, but maybe that will help?
--
Edit: When I say topmost view... probably set all subviews of the UIControl to setUserInteractionEnabled:NO
Check frames of all parent views. The rule is that if sub-view (or its part) of the view is outside the view bounds, it doesn't receive touch events.
Xcode 12 and Latter.
As mention in the accepted answer, make sure all the subviews of the
UIControl view get "User Interaction Enabled" unchecked.
Select your
UIControl view and switch to the "Connection Inspector" and make
sure it has been connected to "Touch Up Inside" event. Sometimes
Xcode uses "Value Changed" event so make sure to change to "Touch
Up Inside" event
It is possible that another view is covering your UIControl, and preventing it from receiving the touch events. Another possibility is that userInteractionEnabled is set to NO somewhere by mistake.
EDIT: I see that you added more code above. Did you verify that your view's frame has a width and height greater than zero? It looks to me like you are calling "size" directly on an object from NSArray (which is 'id'). I don't know how you are doing this without a cast (perhaps the parenthesis didn't come through above?) but if you are somehow pulling it off I wouldn't be surprised if it was an invalid value.
First, why do you call [super touchesBegan:touches withEvent:event];? This call is forwarding the event to the next responder. Usually you do it only when you don't want to handle the event. Are you sure you know what are you doing there?
An idea why it doesn't work - can it be that you have a gesture recognizer which handles the event first?
Anyway, do you really need to override touchesBegan? UIControl is made to track the touch events by itself and call your handlers in response. And the UIControl docs say HOW to subclass it.
Subclassing Notes
You may want to extend a UIControl subclass for either of two reasons:
To observe or modify the dispatch of action messages to targets for particular events
To do this, override sendAction:to:forEvent:, evaluate the passed-in selector, target object, or UIControlEvents bit mask, and proceed as required.
To provide custom tracking behavior (for example, to change the highlight appearance)
To do this, override one or all of the following methods: beginTrackingWithTouch:withEvent:, continueTrackingWithTouch:withEvent:, endTrackingWithTouch:withEvent:.
Possibilities:
You might have forgot to set the delegate for the UIControl.
The other UIControl which receives the touch is obscuring/covering over the UIControl.
Fair enough. Recently re-investigated this (UIControl is very poorly documented) and realised that tracking touches is a replacement for touchesBegan/Ended, not an additional, so sorry about that. I'd reverse the vote but it wouldn't let me :(
You might need to override intrinsicContentSize inside your UIControl subclass.
override var intrinsicContentSize: CGSize {
return CGSize(width: 150, height: 100)
}
I don't quite understand how it fixes it or why, but it works. It doesn't even need to be the exact size as your control.
UIView instances are unable to respond to events so there is no way, up to your current configuration, to trigger your action method ?
Try to change the class of the view to be an instance of the UIControl class !

SubViews not properly initialized using IB

I'm having trouble getting my SubViews properly initialized using Interface Builder. I have the following View hierarchy (UIWindow -> BlankCanvasView -> StalkerView). BlankCanvasView is a subclass of UIView and StalkerView is a IBOutlet of BlankCanvasView
#interface BlankCanvasView : UIView {
IBOutlet UIView *stalker;
}
#end
I've established a connection between the stalker outlet of BlankCanvasView and the subview. However, in my touchesBegin method of BlankCanvasView the stalker outlet is nil. See below for touchesBegin.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Touch Begin detected!!!");
NSLog(#"Stalker instance %#", stalker);
[UIView beginAnimations:#"StalkerAnimation" context:nil];
UITouch *touch = [touches anyObject];
//stalker is nil here!!!
[stalker setCenter:[touch previousLocationInView:self]];
[UIView commitAnimations];
}
What am I missing? It looks like none of my demo apps are properly loading any subviews when I try and follow examples on iTunesU.
If you are creating your 'stalker' view in IB, it must be part of the view hierarchy (that is, added as a subview of another view), or it must be retained by your code in order for it to not be released after loading. If you use a retain property for your stalker variable, this will be taken care of for you, automatically:
#interface BlankCanvasView : UIView
{
UIView *stalker;
}
#property (nonatomic, retain) IBOutlet UIView *stalker;
#end
Make sure you #synthesize stalker; in your BlankCanvasView's implementation, and set the property to nil when you're deallocating your view:
#implementation BlankCanvasView
#synthesize stalker;
- (void)dealloc
{
self.stalker = nil;
[super dealloc];
}
#end