I have a color map within a UIScrollView and am trying to sample the color of a pixel of this map. The sample reticle is positioned above the scrollview, while the user moves the contents of the scrollview under the reticle.
The user can drop the reticle with a tap gesture, but I would like to offer an extra option of moving the view under the reticle.
I'm trying to find out how I can understand what x,y coordinate of the zoomed view is currently under the reticle. The logic for this so far eludes me, especially since zooming in/out is involved.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGPoint mapLocation = [tapGestureRecognizer locationInView:self.surfaceMap];
NSLog(#"mapLocation (x,y) %.0f,%.0f",mapLocation.x,mapLocation.y);
NSLog(#"contentOffset (x,y) %.0f,%.0f",self.scrollView.contentOffset.x,self.scrollView.contentOffset.y);
//calculate where the marker is pointing to in the surface map while the scrollview is scrolling
int frameWidth = self.surfaceMap.frame.size.width;
int frameHeight = self.surfaceMap.frame.size.height;
//this is what I'm trying to calculate
CGPoint trueLocation = CGPointMake(self.scrollView.contentOffset.x+frameWidth-self.surfaceMap.frame.origin.x, self.scrollView.contentOffset.y-self.surfaceMap.frame.origin.y);
NSLog(#"trueLocation (x,y) %.0f,%.0f",trueLocation.x,trueLocation.y);
[self colorOfPixelAtPoint:trueLocation];
}
Any input is appreciated!
You may want to a have look at these two methods in UIView:
- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view;
- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view;
Related
I have the following set-up:
Where the light blue view, let's call it parentView, has a rectangular subview (the purple view) called childView. The user can use pan touches to rotate and stretch childView by putting their finger on the point exhibited by the red dot and pushing it or pulling it.
It's possible that the childView could be scaled small enough to that after the user is finished with its touches, the point denoted by the red dot would be inside of the parentView.
My goal is to create a method that can detect if the red point is in the parentView or not. I've written the following code:
CGPoint childViewRedPoint = CGPointMake(self.bounds.size.width, self.bounds.size.height / 2);
CGPoint rotatedChildViewRedPoint = CGPointApplyAffineTransform(childViewRedPoint, CGAffineTransformMakeRotation(self.rotateAngle));
CGPoint convertedChildViewRedPoint = [self convertPoint:rotatedChildViewRedPoint toView:self.superview];
if (CGRectContainsPoint(self.superview.bounds, convertedChildViewRedPoint))
{
return YES;
}
else
{
return NO;
}
First I find the red point as defined within the childView, then I rotate it by the amount that the view has been rotated, then I convert it to be in the parentViews coordinates.
The points I'm getting don't seem to make sense and this isn't working. Was wondering if anyone knows where I'm going wrong here? Am I not taking parentViews superview into account?
I am not 100% sure, but I think that convertPoint: already takes a rotation (or any other transformation) into account, so you only need:
CGPoint childViewRedPoint = CGPointMake(self.bounds.size.width, self.bounds.size.height / 2);
CGPoint convertedChildViewRedPoint = [self convertPoint:childViewRedPoint toView:self.superview];
if (CGRectContainsPoint(self.superview.bounds, convertedChildViewRedPoint))
...
I'm a beginner to the iOS development, and I'm trying to display an image (a map in fact) which need to be zoomable and pannable. My problem is that I don't know how to add an overlay which will follow the pan to always represent the same location, but won't be scaled by the zoom. There will be several overlays, and each one must be clickable.
To zoom and pan the image (map), I added my UIImageView in a UIScrollView and it works great, but I have no idea how to add this feature.
I found this thread but it is not really the same problem since his overlay is static :
UIScrollview with two images - Keeping 1 image zoomable and 1 image static (fixed size)
I've developped the same application in android, and to make this work I was converting a pixel of the map in screen coordinates thanks to the transformation matrix, and I overrode the onDraw method to display it, but I don't know how to do the same on iOS, and I don't know if this is the better solution.
Thanks for any help.
Ok so I found a way to do it, if it can helps anybody :
I added my overlay in the scrollView, with the background image (the map).
+CustomScrollView
----> UIImageView (map)
----> OverlayImageView (overlay)
In order to zoom, the custom scrollview need a delegate with the following methods :
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
//The background image with the map
return mapView;
}
//When a zoom occurs, move the overlay
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
UIImageView* overlayView = [scroll.subviews objectAtIndex:1];
float x;
float y;
float width;
float height;
//keep the width and height of the overlay
width = overlayView.frame.size.width;
height = overlayView.frame.size.height;
//Move it to stay over the same pixel of the map, and centers it
x = (self.overlay.point.x * scroll.zoomScale - width/2);
y = (self.overlay.point.y * scroll.zoomScale - height/2);
overlayView.frame = CGRectMake(x,y,width,height);
}
With this, we are saying that the zoom only occurs on the background image, but as the overlay is in the UIScrollView, it pans with it. So the only thing we need to care is to move the Overlay when the zoom change, and we know it with the scrollViewDidZoom method.
To handle the touch events, we override the touchEnded:withEvent: of CustomScrollView and we forward it to the overlay if there is only one tap. I don't show the OverlayImageView since it only override this same method (toucheEnded:withEvent:) to handle a touch on it.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
// Coordinates in map view
CGPoint point = [touch locationInView:[self.subviews objectAtIndex:0]];
//forward
if(touch.tapCount == 1){
OverlayImageView* overlayView = [self.subviews objectAtIndex:1];
CGPoint newPoint = [touch locationInView:overlayView];
BOOL isInside = [overlayView pointInside:newPoint withEvent:event];
if(isInside){
[overlayView touchesEnded:touches withEvent:event];
}
}
// zoom
else if(touch.tapCount == 2){
if(self.zoomScale == self.maximumZoomScale){
[self setZoomScale:[self minimumZoomScale] animated:YES];
} else {
CGRect zoomRect = [self zoomRectForScrollView:self withScale:self.maximumZoomScale withCenter:point];
[self zoomToRect:zoomRect animated:YES];
//[self setZoomScale:[self maximumZoomScale] animated:YES];
}
[self setNeedsDisplay];
}
}
Hope this will help.
You can put an imageView on top as an overLay and set its userInteractionEnabled property to NO. Then you have to pan it programmatically.
I ve set of images (created dynamically) which are placed in a single row in my UIScrolView.. I want to trace the changes in the locations (i.e I want to get the x,y position of all the images)while scrolling ....
Things I ve tried:
1.I stored all the images in an array
2.Then I printed the locations of images in this delegate
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
But I can't get result what I want....Instead of that I get the positions of the images which I specified.....How can I get rid of this????
Thanks
I'm not entirely sure I understand what you mean by the location of the images but I think you want the contentOffset property of UIScrollView. If you subtract the x and y values of that from the offset of the frames of your images then you will effectively get where the images are relative to your UIScrollView's parent.
Edit: Here's some code to get you started:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint offset = scrollView.contentOffset;
for (UIView *view in scrollView.subviews) {
CGPoint thisOrigin = view.frame.origin;
thisOrigin.x -= offset.x;
thisOrigin.y -= offset.y;
NSLog(#"thisOrigin = %#", NSStringFromCGPoint(thisOrigin));
}
}
That will get the origin of the images in the coordinate system of the UIScrollView's bounds. If you want just the origin of the images within the coordinate system of the UIScrollView's contents then you just want view.frame.origin itself. But I don't think that's what you want since you said "But I can't get result what I want....Instead of that I get the positions of the images which I specified".
I'd like to move a view from a scrollview to a uiview.
I'm having trouble changing it's center(or frame) so that it remains in the same position in screen (but in a different view, possibly the superview of scrollview).
How should I convert the view's center/frame?
Thank you.
EDIT:
CGPoint oldCenter = dragView.center;
CGPoint newCenter = [dragView convertPoint: oldCenter toView: self.navigationView.contentView];
dragView.center = newCenter;
[self.navigationView.contentView addSubview: dragView];
I can also use (NSSet*) touches since i'm in touchesBegan:
I was having hard time to make it work but the doc wasn't so clear to me.
You can use convertPoint:toView: method of UIView. It is used to convert a point from one view's coordinate system to another. See Converting Between View Coordinate Systems section of UIView class reference. There are more methods available.
-edit-
You are using the wrong point when calling convertPoint: method. The given point should be in dragView's coordinate system where as dragView.center is in its superview's coordinate system.
Use the following point and it should give you the center of dragView in its own coordinate system.
CGPoint p;
p = CGPointMake(dragView.bounds.size.width * 0.5, dragView.bounds.size.height * 0.5);
I'm having difficulties getting a tiled UIScrollView to zoom in and out correctly with pinch zooming. The issue is that when a pinch-zoom occurs, the resulting view is usually not centered in the same region.
Details: The app starts with a tiled image that is 500x500. If a user zooms in, it will snap to 1000x1000 and the tiles will redraw. For all the zoom affects, etc. I am just letting the UIScrollView do it's thing. When scrollViewDidEndZooming:withView:atScale: is called, I redraw the tiles (like you can see in many examples and other questions here).
I think that I've drilled the problem down to calculating the center of the view correctly when I get to scrollViewDidEndZooming:withView:atScale: (I can center on a known point fine after I redraw).
What I'm currently using:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
// as an example, create the "target" content size
CGSize newZoomSize = CGSizeMake(1000, 1000);
// get the center point
CGPoint center = [scrollView contentOffset];
center.x += [scrollView frame].width / 2;
center.y += [scrollView frame].height / 2;
// since pinch zoom changes the contentSize of the scroll view, translate this point to
// the "target" size (from the current size)
center = [self translatePoint:center currentSize:[scrollView contentSize] newSize:newZoomSize];
// redraw...
}
/*
Translate the point from one size to another
*/
- (CGPoint)translatePoint:(CGPoint)origin currentSize:(CGSize)currentSize newSize:(CGSize)newSize {
// shortcut if they are equal
if(currentSize.width == newSize.width && currentSize.height == newSize.height){ return origin; }
// translate
origin.x = newSize.width * (origin.x / currentSize.width);
origin.y = newSize.height * (origin.y / currentSize.height);
return origin;
}
Does this seem correct? Is there a better way? Thanks!
The way I have solved this so far is to store the initial center point of the view when the zoom starts. I initially saving this value when the scrollViewDidScroll method is called (and the scroll view is zooming). When scrollViewDidEndZooming:withView:atScale: is called, I use that center point (and reset the saved value).
The center of the scrollview can be found by adding it's center property, and it's contentOffset property.
aView.center = CGPointMake(
self.scrollView.center.x + self.scrollView.contentOffset.x,
self.scrollView.center.y + self.scrollView.contentOffset.y);