I have the following hierarchy:
VC -> UIView -> UISCrollView
the UIView happens to be the delegate of the UIScrollView, and owns the UIScrollView as an instance variable.
UIView .h:
#import <UIKit/UIKit.h>
#import "ImageScrollView.h"
#interface Scroller : UIView <UIScrollViewDelegate>{
ImageScrollView *scroller;
}
#end
.m:
#import "Scroller.h"
#implementation Scroller
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
DLog(#"");
[self initScroll];
}
return self;
}
-(void)initScroll{
DLog(#"");
scroller = [[ImageScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 200, 200)];
scroller.delegate = self;
[self addSubview:scroller];
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
// Update the page when more than 50% of the previous/next page is visible
CGFloat pageWidth = self.frame.size.width;
int page = floor((scroller.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
DLog(#"%d",page);
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
DLog(#"");
}
Note: as of now, none of the delegate methods get called. I see the UIScrollView on the iphone screen as a black box, however.
#import "ImageScrollView.h"
#implementation ImageScrollView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
DLog(#"%f %f",frame.size.width,frame.size.height);
[self configUI];
[self configSelf];
}
return self;
}
-(void)configUI{
self.backgroundColor = [UIColor blackColor];
self.contentSize = CGSizeMake(1000, 400);
self.showsHorizontalScrollIndicator = YES;
}
-(void)configSelf{
self.scrollEnabled = YES;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
DLog(#"");
for (UITouch *touch in touches) {
CGPoint _location = [touch locationInView:touch.view];
[self processTouch:_location];
}
[self.delegate scrollViewDidEndDecelerating:self];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
DLog(#"");
for (UITouch *touch in touches) {
CGPoint _location = [touch locationInView:touch.view];
[self processTouch:_location];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
DLog(#"");
for (UITouch *touch in touches) {
CGPoint _location = [touch locationInView:touch.view];
[self processTouch:_location];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
Note: none of the touchesEnded or touchesMoved methods get called either in the IUScrollViewSubclass.
What am I doing wrong?
This code works however if I assign the view controller as the delegate to the uiscrollview
If I implement your code, it works fine.
To check more closely what is happening, I added this line:
NSLog (#"%#",NSStringFromSelector(_cmd));
at the start of every method.
If I pan inside the scrollView this is the log:
ScrollViewTest1[4155:707] scrollViewDidScroll:
ScrollViewTest1[4155:707] 0
ScrollViewTest1[4155:707] scrollViewDidScroll:
ScrollViewTest1[4155:707] 0
ScrollViewTest1[4155:707] scrollViewDidScroll:
ScrollViewTest1[4155:707] 0
ScrollViewTest1[4155:707] scrollViewDidEndDecelerating:
Your touches methods don't get called as the touches are intercepted by the scrollView's built-in panGestureRecognizer.
If I tap inside the scrollView this is the log:
ScrollViewTest1[4155:707] touchesBegan:withEvent:
ScrollViewTest1[4155:707] processTouch:
ScrollViewTest1[4155:707] scrollViewDidEndDecelerating:
ScrollViewTest1[4155:707] touchesEnded:withEvent:
ScrollViewTest1[4155:707] processTouch:
Touches called as the scrollView isn't grabbing the touch events for it's own gestureRecognizer.
i am using ktphotobrowser for my app. in big image screen (ktphotoscrollviewcontroller) when i zoom image with two times tapping, the content size of scrollview (ktphotoview - it is an uiscrollview object) grows to bound.size * maximumzoomscale but my zoomed image size still smaller than contentsize of scrollview. both zoomed image size and the scrollview's contentsize must be same so where can i do these calculation on the code?
if anybody use ktphotobrowser could help me about this problem, i will very appreciate.
i find the tap count but after this code i couldnt find where is the contentsize growing (zoom) or image growing (zoom) code
below you can find the whole code of ktphotoview.h page where the tapping count and zooming actions are coded:
#import "KTPhotoView.h"
#import "KTPhotoScrollViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface KTPhotoView (KTPrivateMethods)
- (void)loadSubviewsWithFrame:(CGRect)frame;
- (BOOL)isZoomed;
- (void)toggleChromeDisplay;
#end
#implementation KTPhotoView
#synthesize scroller = scroller_;
#synthesize index = index_;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setDelegate:self];
[self setMaximumZoomScale:2.0];
[self setShowsHorizontalScrollIndicator:NO];
[self setShowsVerticalScrollIndicator:NO];
[self loadSubviewsWithFrame:frame];
NSLog(#"scrollview2 %f",self.contentSize.height);
}
return self;
}
- (void)loadSubviewsWithFrame:(CGRect)frame
{
imageView_ = [[UIImageView alloc] initWithFrame:frame];
[imageView_ setContentMode:UIViewContentModeScaleAspectFit];
[self addSubview:imageView_];
}
- (void)setImage:(UIImage *)newImage
{
[imageView_ setImage:newImage];
}
- (void)layoutSubviews
{
[super layoutSubviews];
if ([self isZoomed] == NO && CGRectEqualToRect([self bounds], [imageView_ frame]) == NO) {
[imageView_ setFrame:[self bounds]];
}
}
- (void)toggleChromeDisplay
{
if (scroller_) {
[scroller_ toggleChromeDisplay];
}
}
- (BOOL)isZoomed
{
return !([self zoomScale] == [self minimumZoomScale]);
}
- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center
{
CGRect zoomRect;
// the zoom rect is in the content view's coordinates.
// At a zoom scale of 1.0, it would be the size of the imageScrollView's bounds.
// As the zoom scale decreases, so more content is visible, the size of the rect grows.
zoomRect.size.height = [self frame].size.height / scale;
zoomRect.size.width = [self frame].size.width / scale;
// choose an origin so as to get the right center.
zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0);
zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0);
NSLog(#"zoomRectHeight %f",[self frame].size.height);
return zoomRect;
}
- (void)zoomToLocation:(CGPoint)location
{
float newScale;
CGRect zoomRect;
if ([self isZoomed]) {
zoomRect = [self bounds];
} else {
newScale = [self maximumZoomScale];
zoomRect = [self zoomRectForScale:newScale withCenter:location];
}
// NSLog(#"zoomRectHeight %f",location.);
[self zoomToRect:zoomRect animated:YES];
}
- (void)turnOffZoom
{
if ([self isZoomed]) {
[self zoomToLocation:CGPointZero];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if ([touch view] == self) {
if ([touch tapCount] == 2) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(toggleChromeDisplay) object:nil];
[self zoomToLocation:[touch locationInView:self]];
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if ([touch view] == self) {
if ([touch tapCount] == 1) {
[self performSelector:#selector(toggleChromeDisplay) withObject:nil afterDelay:0.5];
}
}
}
#pragma mark -
#pragma mark UIScrollViewDelegate Methods
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
UIView *viewToZoom = imageView_;
NSLog(#"scrollview %f",self.contentSize.height);
return viewToZoom;
}
i use the below code in ktphotoview.m file to solve my problem hope this helps:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if ([touch view] == self) {
if ([touch tapCount] == 2) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(toggleChromeDisplay) object:nil];
[self zoomToRect:[self zoomRectForScale:[self maximumZoomScale] withCenter:[touch locationInView:self]] animated:YES];
float abc=(self.contentSize.height-(imageView_.image.size.height*320/imageView_.image.size.width*self.maximumZoomScale))/2 *-1;
[self setContentInset:UIEdgeInsetsMake(abc, 0, abc, 0)];
}
}
}
I have a UIScrollView which I have set up to swipe one column at a time (two columns per page) - by setting the frame to half of the views actual width, setting clipToBounds to NO and use hitTest to declare the area outside of the frame as belonging to the UIScrollView (see example below).
This works great, but my problem now is that the subviews of the UIScrollView don't get any touch events - only the main UIScrollView does.
In the following example, if the hitTest code is included, then the scrollview scrolls correctly, paging one column at a time and all its content may be seen - but the inner scrollviews do not receive touch events.
If I remove the hitTest code, then only the first child scrollview receives touches, and all its content may be seen - but the main scrollview wont get touches in the non-clipped area.
How can I solve this?
Example:
//=========================================
// UIScrollViewEx
// Just in order to log touches...
//=========================================
#interface UIScrollViewEx : UIScrollView {}
#end
#implementation UIScrollViewEx
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Touches Began (0x%08X)", (unsigned int)self);
}
#end
//=========================================
// UIViewEx
// Dummy class - sets subview as hit target
// just to demonstrate usage of non-clipped
// content
//=========================================
#interface UIViewEx : UIView {}
#end
#implementation UIViewEx
- (UIView *) hitTest:(CGPoint) point withEvent:(UIEvent *)event {
if ([self pointInside:point withEvent:event]) {
return [self.subviews objectAtIndex:0];
}
return nil;
}
#end
//=========================================
// MainClass
// Any UIViewEx based class which returns
// the UIScrollView child on hittest
//=========================================
#implementation MyClass
- (UIColor*) randomColor
{
int r = arc4random() % 100;
int g = arc4random() % 100;
int b = arc4random() % 100;
return [UIColor colorWithRed:(0.01 * r) green:(0.01 * g) blue:(0.01 * b) alpha:1.0];
}
- (void) loadScrollviews
{
// Set frame to half of actual width so that paging will swipe half a page only
CGRect frame = CGRectMake(0, 0, self.bounds.size.width / 2, 400);
// Main scrollview
UIScrollView *scrollview = [[UIScrollView alloc] initWithFrame:frame];
[scrollview setBackgroundColor:[UIColor greenColor]];
[scrollview setPagingEnabled:YES];
[scrollview setClipsToBounds:NO];
// Create smaller scrollviews inside it - each one half a screen wide
const int numItems = 5;
for(int i = 0; i < numItems; ++i) {
frame.origin.x = frame.size.width * i;
UIScrollView *innerScrollview = [[UIScrollViewEx alloc] initWithFrame:frame];
[innerScrollview setContentSize:CGSizeMake(frame.size.width, 1000)];
[innerScrollview setBackgroundColor:[self randomColor]];
[scrollview addSubview:innerScrollview];
[innerScrollview release];
}
[scrollview setContentSize:CGSizeMake(numItems * frame.size.width, frame.size.height)];
[self addSubview:scrollview];
}
#end
Update
I get the touches forwarded to the inner view by doing the following, but surely there must be a better way?
- (UIView *) hitTest: (CGPoint) pt withEvent: (UIEvent *) event
{
if(CGRectContainsPoint(self.bounds, pt))
{
UIScrollView *scrollview = [self.subviews objectAtIndex:0];
CGPoint scrollViewpoint = [scrollview convertPoint:pt fromView:self];
for(UIView *view in scrollview.subviews) {
if(CGRectContainsPoint(view.frame, scrollViewpoint)) {
return view;
}
}
return scrollview;
}
else {
return [super hitTest:pt withEvent:event];
}
}
This may work:
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
UIView* child = nil;
if ((child = [super hitTest:point withEvent:event]) == self)
return self.scrollView;
return child;
}
But if subview is out of scrollview bounds events were not fired and this function returns self.scrollView.
alt text http://www.gisnotes.com/wordpress/wp-content/uploads/2009/09/poly.png
In a nutshell, I am trying to figure out how to scale the geometry (point, line, and polygon) implemented in a custom view (geometryView) on top of MKMapView (mapView).
What I did was..
Create DrawMapViewController. Add the UIBarButtonItems (MAP, PT, LN, PG) on the bottom toolbar.
When you click on map button, you are able to pan/zoom on the Map. This enables the mapView by setting the geometryView.hidden = YES;
When any of the three geometry buttons is clicked, the geometryView is displayed by geometryView.hidden = NO, thus, enabling touchEvents and drawing the geometry from GeometryView.drawRect's method.
Layer ordering is as follows: mapView is at the bottom of geometryView.
-geometryView
-mapView
What is my problem?
Ideally, during "map" mode and when the user is panning and zooming, I am hoping if its possible to display the drawing from geometryView. But when the user hits "map", then geometryView.hidden = YES, thus the drawing disappears. If I make geometryView visible, then the user interacts with geometryView not mapView, so there's no zooming and panning.
Is it possible to handle touchEvents (pan/zoom) of MKMapView below a custom view while the custom View is displayed? Any other ideas/approaches is very much appreciated.
Thanks,
Rupert
GeometryView Listing:
#synthesize mapview, pinFactory;
- (id)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
NSLog(#"DrawRect called");
CGContextRef context = UIGraphicsGetCurrentContext();
// Drawing lines with a white stroke color
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, 2.0);
if(pinFactory.geometryState == 2){ //Draw Line
if( [pinFactory actualPinCount] > 1){
Pin *pin1 = (Pin *)[[pinFactory pinArray] objectAtIndex:0];
CGPoint pt1 = pin1.touchLocation;
CGContextMoveToPoint(context, pt1.x, pt1.y);
for (int i = 1; i < ([pinFactory actualPinCount]); i++)
{
Pin *pin2 = (Pin *)[[pinFactory pinArray] objectAtIndex:i];
CGPoint pt2 = pin2.touchLocation;
CGContextAddLineToPoint(context, pt2.x, pt2.y);
}
CGContextStrokePath(context);
}
}
else if(pinFactory.geometryState == 3){ //Draw Polygon
//if there are two points, draw a line first.
//if there are three points, fill the polygon
if( [pinFactory actualPinCount] == 2){
Pin *pin1 = (Pin *)[[pinFactory pinArray] objectAtIndex:0];
CGPoint pt1 = pin1.touchLocation;
CGContextMoveToPoint(context, pt1.x, pt1.y);
Pin *pin2 = (Pin *)[[pinFactory pinArray] objectAtIndex:1];
CGPoint pt2 = pin2.touchLocation;
CGContextAddLineToPoint(context, pt2.x, pt2.y);
CGContextStrokePath(context);
}
else if([pinFactory actualPinCount] > 2){
//fill with a blue color
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
Pin *pin1 = (Pin *)[[pinFactory pinArray] objectAtIndex:0];
CGPoint pt1 = pin1.touchLocation;
CGContextMoveToPoint(context, pt1.x, pt1.y);
for (int i = 1; i < ([pinFactory actualPinCount]); i++)
{
Pin *pin2 = (Pin *)[[pinFactory pinArray] objectAtIndex:i];
CGPoint pt2 = pin2.touchLocation;
CGContextAddLineToPoint(context, pt2.x, pt2.y);
}
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
}
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self setNeedsDisplay];
UITouch* aTouch = [touches anyObject];
location = [aTouch locationInView:self];
NSLog(#"touchesBegan: x:%f, y:%f", location.x, location.y );
CLLocationCoordinate2D coordinate = [mapview convertPoint:location toCoordinateFromView:self];
switch (pinFactory.geometryState) {
case 1:{
if( [pinFactory actualPinCount] == 1){
//[UIView beginAnimations:#"stalk" context:nil];
//[UIView setAnimationDuration:1];
//[UIView setAnimationBeginsFromCurrentState:YES];
Pin *pin = (Pin *)[pinFactory getObjectAtIndex:0];
[mapview removeAnnotation:pin];
[pinFactory removeObject:pin];
Pin *newPin = [[Pin alloc] initWithCoordinate:coordinate initLocation:location withTitle:#"My Pin"];
[pinFactory addObject:newPin];
[mapview addAnnotation:newPin];
[newPin release];
//[UIView commitAnimations];
}
else{
//Lets add a new pin to the geometry
Pin *pin = [[Pin alloc] initWithCoordinate:coordinate initLocation:location withTitle:#"My Pin"];
[pinFactory addObject:pin];
[mapview addAnnotation:pin];
[pin release];
}
break;
}
case 2:{
//Lets add a new pin
Pin *pin = [[Pin alloc] initWithCoordinate:coordinate initLocation:location withTitle:#"My Pin"];
[pinFactory addObject:pin];
[mapview addAnnotation:pin];
[pin release];
[self setNeedsDisplay];
break;
}
case 3:{
//Lets add a new pin
Pin *pin = [[Pin alloc] initWithCoordinate:coordinate initLocation:location withTitle:#"My Pin"];
[pinFactory addObject:pin];
[mapview addAnnotation:pin];
[pin release];
[self setNeedsDisplay];
break;
}
default:
break;
}
}
- (void)dealloc {
[super dealloc];
}
#end
DrawMapViewController Listing:
#import "DrawMapViewController.h"
#import "Pin.h"
#implementation DrawMapViewController
#synthesize mapview, mapBarButton, pointBarButton, lineBarButton, polygonBarButton, geometryView;
/* State represents state of the map
* 0 = map
* 1 = point
* 2 = line
* 3 = polygon
*/
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
self.title = #"Map";
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
mapview.mapType = MKMapTypeSatellite;
NSMutableArray *pinArray = [[NSMutableArray alloc] initWithObjects:nil];
pinFactory = [[PinFactory alloc] initWithArray:pinArray];
pinFactory.map = mapview;
[pinArray release];
geometryView = [[GeometryView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 372.0f)];
geometryView.pinFactory = pinFactory;
geometryView.mapview = mapview;
geometryView.backgroundColor = [UIColor clearColor];
[self.view addSubview:geometryView];
[self changeButtonAndViewState:0];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[mapBarButton release];
[pointBarButton release];
[lineBarButton release];
[polygonBarButton release];
[super dealloc];
}
- (IBAction)mapBarButtonPressed{
NSLog(#"mapBarButtonPressed");
[self changeButtonAndViewState:0];
}
- (IBAction)pointBarButtonPressed{
NSLog(#"pointBarButtonPressed");
[self changeButtonAndViewState:1];
if( [pinFactory actualPinCount] > 0){
[self resetGeometry];
}
}
- (IBAction)lineBarButtonPressed{
NSLog(#"lineBarButtonPressed");
if( [pinFactory actualPinCount] > 0){
[self resetGeometry];
}
[self changeButtonAndViewState:2];
}
- (IBAction)polygonBarButtonPressed{
NSLog(#"polygonBarButtonPressed");
if( [pinFactory actualPinCount] > 0){
[self resetGeometry];
}
[self changeButtonAndViewState:3];
}
- (void)resetGeometry{
NSLog(#"resetting geometry.. deleting all pins");
[mapview removeAnnotations:[pinFactory pinArray]];
NSMutableArray *array = [pinFactory pinArray];
[array removeAllObjects];
[geometryView setNeedsDisplay];
}
- (void)changeButtonAndViewState:(int)s{
[pinFactory setGeometryState:s];
mapBarButton.style = UIBarButtonItemStyleBordered;
pointBarButton.style = UIBarButtonItemStyleBordered;
lineBarButton.style = UIBarButtonItemStyleBordered;
polygonBarButton.style = UIBarButtonItemStyleBordered;
pointBarButton.enabled = YES;
lineBarButton.enabled = YES;
polygonBarButton.enabled = YES;
switch ([pinFactory geometryState]) {
case 0:{
mapBarButton.style = UIBarButtonItemStyleDone;
geometryView.hidden = YES;
break;
}
case 1:{
pointBarButton.enabled = NO;
pointBarButton.style = UIBarButtonItemStyleDone;
geometryView.hidden = NO;
break;
}
case 2:{
lineBarButton.enabled = NO;
lineBarButton.style = UIBarButtonItemStyleDone;
geometryView.hidden = NO;
break;
}
case 3:{
polygonBarButton.enabled = NO;
polygonBarButton.style = UIBarButtonItemStyleDone;
geometryView.hidden = NO;
break;
}
default:
break;
}
}
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animate{
NSLog(#"regionDidChangeAnimated");
}
#end
Hey I see nobody has answered you, and I just figured out how to do this. Unfortunately you cannot intercept events and forward them to the mapView so something like
#interface MapOverlay
MKMapView* map;
#end
#implementation
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// Do my stuff
foo();
bar();
// Forward to the map
[map touchesBegan....];
}
#end
This is what you want to do but it WILL NOT WORK. For some reason you cannot intercept the map's events, nor can you do the reverse and subclass MKMapView and overload it's touch methods. The only way I have found to do this is as follows.
Create your MapOverlay view, set it to have a transparent background and disable touches to it. Overlay this on top of your MKMapView. Then do the following
Subclass UIWindow with a custom class that will forward all touches to your overlay, either handling the logic as "if the overlay is not hidden, then forward", or in the overlay itself keep state. Anyway it looks like this
#implementation CustomWindow
- (void)sendEvent:(UIEvent*)event
{
[super sendEvent:event];
// Forward the event to the overlay
}
When you are forwarding the event to the overlay, you first check if the touches are inside of your mapView region, then figure out what type of touches they are, then call the correct touch method on your overlay.
Good luck!
I know this answer probably comes too late, but I'll take a stab anyway for the benefit of those (such as myself) who've also come across this problem as well.
The MKMapView class's touch events are all handled by a UIScrollView internally. You can capture the events of this scroll view by making the MKMapView a subview of a custom UIView, and providing your custom touches methods in the custom view.
The trick is to keep track of the UIScrollView used by the MKMapView. To do this, I overrode the "hitTest" method, which returns "self", which I believe means this custom view should handle the touch events.
Also, the hitTest method gets the UIScrollView of the MKMapView. In my custom UIView I called it "echo_to" because the events are then echoed to the UIScrollView to make the map work as it does normally.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// Get the UIView (in this case, a UIScrollView) that
// would ordinarily handle the events
echo_to = [super hitTest:point withEvent:event];
// But let it be known we'll handle them instead.
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Touches Began");
// add your custom annotation behavior here
[echo_to touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Touches moved");
// add your custom annotation behavior here
[echo_to touchesMoved:touches withEvent:event];
}
Best of luck.
Depending on the sophistication of your geometryView, you may be able to use an annotation view to draw it.
The downside to this approach is that the view won't scale with the map (although you can use the mapView:regionDidChangeanimated: delegate method to redraw after a zoom), but it will pan with the map.
The hitTest trick works great as long as you don't need users to be able to tap on annotations, or pinch-and-zoom the map. Neither of these are able to be forwarded.
I am doing multi touch on UImageView means zoom in and zoom out on image view. I am using followng code but it doesn't work very well. Can anyone look at this code,
#import "ZoomingImageView.h"
#implementation ZoomingImageView
#synthesize zoomed;
#synthesize moved;
define HORIZ_SWIPE_DRAG_MIN 24
define VERT_SWIPE_DRAG_MAX 24
define TAP_MIN_DRAG 10
CGPoint startTouchPosition;
CGFloat initialDistance;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
moved = NO;
zoomed = NO;
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void)dealloc {
if([timer isValid])
[timer invalidate];
[super dealloc];
}
- (void) setImage: (UIImage*)img
{
zoomed = NO;
moved = NO;
self.transform = CGAffineTransformIdentity;
[super setImage:img];
}
- (CGFloat)distanceBetweenTwoPoints:(CGPoint)fromPoint toPoint:(CGPoint)toPoint {
float x = toPoint.x - fromPoint.x;
float y = toPoint.y - fromPoint.y;
return sqrt(x * x + y * y);
}
- (CGFloat)scaleAmount: (CGFloat)delta {
CGFloat pix = sqrt(self.frame.size.width * self.frame.size.height);
CGFloat scale = 1.0 + (delta / pix);
return scale;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if([timer isValid])
[timer invalidate];
moved = NO;
switch ([touches count]) {
case 1:
{
// single touch
UITouch * touch = [touches anyObject];
startTouchPosition = [touch locationInView:self];
initialDistance = -1;
break;
}
default:
{
// multi touch
UITouch *touch1 = [[touches allObjects] objectAtIndex:0];
UITouch *touch2 = [[touches allObjects] objectAtIndex:1];
initialDistance = [self distanceBetweenTwoPoints:[touch1 locationInView:self]
toPoint:[touch2 locationInView:self]];
break;
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch1 = [[touches allObjects] objectAtIndex:0];
if([timer isValid])
[timer invalidate];
/*if ([touches count] == 1) {
CGPoint pos = [touch1 locationInView:self];
self.transform = CGAffineTransformTranslate(self.transform, pos.x - startTouchPosition.x, pos.y - startTouchPosition.y);
moved = YES;
return;
}****/
if ((initialDistance > 0) && ([touches count] > 1)) {
UITouch *touch2 = [[touches allObjects] objectAtIndex:1];
CGFloat currentDistance = [self distanceBetweenTwoPoints:[touch1 locationInView:self]
toPoint:[touch2 locationInView:self]];
CGFloat movement = currentDistance - initialDistance;
NSLog(#"Touch moved: %f", movement);
CGFloat scale = [self scaleAmount: movement];
self.transform = CGAffineTransformScale(self.transform, scale, scale);
// }
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch1 = [[touches allObjects] objectAtIndex:0];
if ([touches count] == 1) {
// double tap to reset to default size
if ([touch1 tapCount] > 1) {
if (zoomed) {
self.transform = CGAffineTransformIdentity;
moved = NO;
zoomed = NO;
}
return;
}
}
else {
// multi-touch
UITouch *touch2 = [[touches allObjects] objectAtIndex:1];
CGFloat finalDistance = [self distanceBetweenTwoPoints:[touch1 locationInView:self]
toPoint:[touch2 locationInView:self]];
CGFloat movement = finalDistance - initialDistance;
NSLog(#"Final Distance: %f, movement=%f",finalDistance,movement);
if (movement != 0) {
CGFloat scale = [self scaleAmount: movement];
self.transform = CGAffineTransformScale(self.transform, scale, scale);
NSLog(#"Scaling: %f", scale);
zoomed = YES;
}
}
}
- (void)singleTap: (NSTimer*)theTimer {
// must override
}
- (void)animateSwipe: (int) direction {
// must override
}
It is not working fine on device. can anyone tell that where i am wrong.
When you use any CGAffinTransform.... the value of the frame property becomes undefined. In your code you are using the frame.size.width and frame.size.height to calculate the change in size. After the first iteration of CGAffinTransformScale you would not get the right scale factor. According to the documentation bounds would be the right property for scale calculations.