Is there hitTest:(CGPoint) in CAShapeLayer? - iphone

I have a multiple CAShapeLayers in a view and I'd like to drag it.(shape are irregular) Please give some suggestions for doing it.

CALayer and its subclasses are not part of the responder chain, and they do not descend from UIResponder. Therefore, touchesBegan:withEvent:, etc. will never be called on any CALayer subclass. You need to detect the touch on one of the hosting UIViews in the layer hierarchy. Then, you use hitTest: in the touch handlers to detect which layer was touched.
It will be easier on you if you create a UIView subclass with a CAShapeLayer as its backing layer like this:
#implementation MyShapeView
- (CALayer *)layerClass {
return [CAShapeLayer class];
}
#end
Then, add instances of your custom view as subviews to your main view. After that, you can use the UIResponder methods or, even better, a UIGestureRecognizer to handle the dragging. I highly recommend using gesture recognizers if you can target iOS 3.2 or higher. They make event handling much simpler.

Related

iPhone SDK - Forwarding touches from a UIViewController to a subview

I have a UIViewController with a subclass of UIView on it called customSubView1. Then on customSubView1 I have another subclass of UIView called customSubView2.
I can capture all the events for touches on all the subviews when I put touchesBegan/touchesMoved/etc in the UIViewController class. But I want to be able to process them in my custom classes.
I keep reading that the UIViewController class needs to 'forward' the touch events to the subviews, but I haven't been able to find any example code to do this. Does anyone have any idea?
Many thanks,
Brett
Implement the touches... methods in your views.
Try to set for you UIView object userInteractionEnabled property:
myUIView.userInteractionEnabled = YES;

UIView, how to determine when touches entered the view

It appears that all the touch methods of a UIView are only called if the touches began within the bounds of that view. Is there a way to have a view respond to a user who has touched outside the view, but then dragged his fingers into the view?
In case it matters, my specific application is for dragging a MKPinAnnotationView (using built-in 4.0 dragging). I want something to happen if the user drags a pin onto another view (which happens to be an AnnotationView as well, but it could be anything). No method for dragging is called until I let go of the pin; and no method no the UIView that's being dragged to seems to be called unless I started by touching from within the view.
Because the superview is a MKMapView, it is difficult to just use the touchesMoved event of that and check if the user is in the right location or not. Thanks!
So after playing around with it for a while, I found that the answer given here actually gave me what I needed, even though the question being asked was different.
It turns out you can subclass UIGestureRecognizer; and have it handle all the touches for the view that it has been added to (including an MKMapView). This allows all the normal MKMapView interactions to still behave without any problem; but also alerts me of the touches. In touchesMoved, I just check the location of the touch; and see if it is within the bounds of my other view.
From everything I tried; this seems to be the only way to intercept touchesMoved while the user is dragging an MKAnnotation.
You sure can:
(HitstateView.h)
#import <UIKit/UIKit.h>
#interface HitstateView : UIView {
id overrideObject;
}
#property (nonatomic, retain) id overrideObject;
#end
(HitstateView.m)
#import "HitstateView.h"
#implementation HitstateView
#synthesize overrideObject;
- (void)dealloc {
self.overrideObject = nil;
[super dealloc];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self) {
return overrideObject;
}
return hitView;
}
#end
Make this view the size of your touch area. Set the overideObject to the view you want the touches to go. IIRC it ought to be a subview of the HitstateView.
Every view inherits UIResponder so every view gets touchesBegan/Moved/Ended - I do not think starting the touch outside the view means the view gets no event when the touch moves over the view. If you want to get a notification that something has been dragged onto your MKMapView you should make a subclass that handles the touch but then passes the event to super, allowing the hierarchy to do whatever it needs to do with the touch. You don't need to capture or modify the event just observe it.
It depends on how your views are set up. Generally leveraging the responder chain is the best way to go. It allows you to play tricks, though it may be too specific to address your particular needs.
You can also play tricks with forward events by override hit testing:
http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/MultitouchEvents/MultitouchEvents.html%23//apple_ref/doc/uid/TP40009541-CH3-SW3
Your particular case sounds pretty exotic, so you may have to play tricks like having a parent view whose frame is large enough to contain both views in question.

iPhone: calling a parent/super method from a subview

hope someone can help me on this as been stuck for hours.
I am trying to make a kind of picture book.
I have a view which is my container and I add subviews to that by using addsubview.
On the subview, I have swipe gestures etc that I want to trigger off method in the parent view. I worked out how to trigger the delegate but I cant get the delegate to trigger the parent view. I have read over 10 different ways of doing it and none work.
I now very confused about what a super view is to. Just to confuse matters, the delegate has a tabcontroller and the parent view is tab button 1
I tried
[self.view.superview method]
[self.superview method]
On the delegate I tried
self.tabcontroller.parentviewcontroller, selectedview, super view.super
UPDATE :
The subview needs to be independant of the parent view as its a reusable view.
Also I have not set the parentview to superview as I just thought a superview is a view with subviews (please don't kill me). So maybe I just need to set the parentview to a superview?
The proper way of doing such things is to use protocol and delegate pattern.
Define a protocol like
#protocol subViewDelegate
-(void)somethingHappened:(id)sender;
#end
then implement that protocol in your superview :
#interface superView:UIViewController<subViewDelegate> {
...
}
...
#end
define a delegate property in your SubView like this
#interface subView : UIView {
id<subViewDelegate> delegate;
...
}
#propery (nonatomic, assign) id<subViewDelegate> delegate;
...
#end
the in your subview, call the delegate like this
[self.delegate somethingHappened :self];
It's a little hard to help you without any code given, but let's try:
Create a protocol: Name it however you like (I will call it "MyProtocol") and add to it the definition of the function you want to call in your superview, let's call it "respondToSwipe"
If your superview is a UIView, you have to create your own subclass of UIView and make your superview an instance of that class.
Let your (newly) created superview class implement the protocol of 1.) an implement the "respondToSwipe" method
Create an instance variable of the the type id in your subview, and name it however you like, e.g. "myDelegate".
Pass the superview created in 2/3.) to your "myDelegate" variable
Call [myDelegate respondToSwipe] whenever you like
For a custom view, you could subclass UIControl and use control events:
Define some control events. You're free to make up 4 control events (UIControlEventApplicationReserved = 0x0F000000)
Have whoever wants to receive events call addTarget:action:forControlEvents:
Have the control call [self sendActionsForControlEvents:events]
Alternatively, you could use a UIGestureRecognizer-style interface (addTarget:action:).
Alternatively just use UIGestureRecognizer (OS 3.2+)
Did your parent view set itself to be the superview of the subview when it added the subview? Otherwise the subview doesn't know who its superview is.
The more standard way of naming things to call the method handler the delegate instead of the superview, make it a property, and have the subview check for both the existence of the delegate being set and whether it can handle the method.
Here a very good example of how apply the delegation pattern on the iPhone. I downloaded the code an it works pretty good.
http://www.hivestudio.cat/index.php?option=com_content&view=article&id=57:technical-note-the-delegation-pattern-on-the-iphone&catid=35:technical-note-category&Itemid=76

Is there an easy way to drag and drop a UITableViewCell between two UITableViews?

As the title says. Reordering within a single UITableView is trivial, but the screen of the iPad is large enough to display multiple UITableViews at the same time. So it seems like there should be a way to drag and drop a UITableViewCell between two UITableViews. Any thoughts on the best approach?
My solution was to use a custom UIGestureRecognizer for tracking the touch events, and a separate view for drawing the dragging operation.
This works because UIGestureRecognizer doesn't block the responder chain.
From the UIGestureRecognizer documentation:
UIGestureRecognizer objects are not in the responder chain, yet observe touches hit-tested to their view and their view's subviews.
Create a custom UIViewController (DragAndDropViewController) and add its view to the parent view of the views you want the drag & drop operation to occur in. Use your custom gesture recognizer class to forward the touch information to your DragAndDropViewController.
The source tells your DragAndDropViewController where the the drag originates from (and any custom info). The controller should also have a delegate reference to the drop destination. When the drop occurs, send the delegate the UITouch event (not recommended according to Apple, but the UITouch object is needed to get the correct location in the destination view).
This is what my DragAndDropViewController looks like:
#protocol DragAndDropDestination
(void)droppedItem:(NSDictionary *)item withTouch:(UITouch *)touch;
#end
#interface DragAndDropViewController : UIViewController
{
id _dropDestination;
NSDictionary *_draggedItem;
UIImageView *_icon;
}
#property (nonatomic, assign) id dropDestination;
#property (nonatomic, retain) NSDictionary *draggedItem;
// Source sends this message
- (void)startDraggingWithItem:(NSDictionary *)item;
#end
Once the destination gets the message, you can use - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event to get the exact view at the drop destination.
Also make sure you disable cancelsTouchesInView in the gesture recognizer if you want other UI operations to happen normally in your table views.

CALayer delegate method drawLayer not getting called

My object a sub class of NSObject has CALayer instance variable. I use it for drawing and caching content with its delegate set to my object.
But for some reason drawLayer:inContext: method NEVER gets called. Whereas actionForLayer:forKey: delegate method does get called implying the delegate is getting set properly with layer.delegate = self in the object's init method.
Any suggestions on what is preventing my layer drawing method drawLayer:inContext: from getting called ?
I am called the [layer setNeedDisplay] often. So I guess it is some fundamental error.
drawLayer:inContext: won't get called if your frame is CGRectZero or offscreen. Also, if your CALayer isn't attached to an existing onscreen layer, it will never draw, no matter how many times you call setNeedsDisplay
Implement an empty drawRect:
- (void)drawRect:(CGRect)rect {
}
Taken from the ZoomingPDFViewer project:-
UIView uses the existence of -drawRect: to determine if it should
allow its CALayer to be invalidated, which would then lead to the
layer creating a backing store and -drawLayer:inContext: being
called. By implementing an empty -drawRect: method, we allow UIKit
to continue to implement this logic, while doing our real drawing work
inside of -drawLayer:inContext:
The layer object's setNeedsDisplay must be called. Simply adding the layer as a sublayer does not do that for you. Got this from Ray Wenderlich's CALayer tutorial.
If you're eventually using the CALayer with a UIView then the delegate must be the view object itself:
From the iOS CALayer documentation:
"In iOS, if the layer is associated with a UIView object, this property must be set to the view that owns the layer."
If you have a multi-threaded app where background processing drives the need to update the CALayer, you must call setNeedsDisplay in the main thread