I add custom annotation in a mapview. Based on the internal model of the app the annotations can change color and title (although position may not change). I am using dequeueReusableAnnotationViewWithIdentifierin the method
- (MKAnnotationView *)mapView:(MKMapView *) viewForAnnotation:(id <MKAnnotation>)
The strange thing is that when the model changes the annotations are "refreshed" to use the correct title and color (I just use removeAnnotations: and add the new ones), but later when playing with the map some old annotations with wrong color are dequeued. I use a different identifier each time the model changes.
here is the code:
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation{
// in case it's the user location, we already have an annotation, so just return nil
if ([annotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
if ([annotation isKindOfClass:[APGSAnnotation class]]){
APGSAnnotation *gsn = (APGSAnnotation*) annotation;
NSString *GSAnnotationIdentifier = [NSString stringWithFormat:#"gid_%lu_%#", (unsigned long)gsn.gs.gID, self.car.energyType];
MKAnnotationView *markerView = [theMapView dequeueReusableAnnotationViewWithIdentifier:GSAnnotationIdentifier];
if (markerView == nil) {
MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:GSAnnotationIdentifier];
annotationView.canShowCallout = YES;
annotationView.image = [self customizeAnnotationImage:gsn.gs];
annotationView.opaque = NO;
UIImageView *sfIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:gsn.logo]];
annotationView.leftCalloutAccessoryView = sfIconView;
// http://stackoverflow.com/questions/8165262/mkannotation-image-offset-with-custom-pin-image
annotationView.centerOffset = CGPointMake(0,-annotationView.image.size.height/2);
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:nil action:nil forControlEvents:UIControlEventTouchUpInside];
annotationView.rightCalloutAccessoryView = rightButton;
return annotationView;
}else{
markerView.annotation = annotation;
return markerView;
}
}
return nil;
}
and the customize method
- (UIImage*)customizeAnnotationImage:(APGS*)gs{
UIImage *markerImage;
if (gs.gID == self.best.gID) {
markerImage = [UIImage imageNamed:#"marker_red.png"];
}else if (gs.type == k1){
markerImage = [UIImage imageNamed:#"marker_blue.png"];
}else if (gs.type == k2){
markerImage = [UIImage imageNamed:#"marker_green.png"];
}else if (gs.type == k3){
markerImage = [UIImage imageNamed:#"marker_purple.png"];
}else if (gs.type == k4){
markerImage = [UIImage imageNamed:#"marker_brown.png"];
}
UIImage *logoImage = [UIImage imageNamed:gs.logo];
// size the flag down to the appropriate size
CGRect resizeRect;
resizeRect.size = markerImage.size;
CGSize maxSize = CGRectInset(self.view.bounds, kAnnotationPadding, kAnnotationPadding).size;
maxSize.height -= self.navigationController.navigationBar.frame.size.height + kCallOutHeight;
if (resizeRect.size.width > maxSize.width)
resizeRect.size = CGSizeMake(maxSize.width, resizeRect.size.height / resizeRect.size.width * maxSize.width);
if (resizeRect.size.height > maxSize.height)
resizeRect.size = CGSizeMake(resizeRect.size.width / resizeRect.size.height * maxSize.height, maxSize.height);
resizeRect.origin = CGPointMake(0.0, 0.0);
float initialWidth = resizeRect.size.width;
UIGraphicsBeginImageContextWithOptions(resizeRect.size, NO, 0.0f);
[markerImage drawInRect:resizeRect];
resizeRect.size.width = resizeRect.size.width/2;
resizeRect.size.height = resizeRect.size.height/2;
resizeRect.origin.x = resizeRect.origin.x + (initialWidth - resizeRect.size.width)/2;
resizeRect.origin.y = resizeRect.origin.y + kLogoHeightPadding;
[logoImage drawInRect:resizeRect];
// Create string drawing context
UIFont *font = [UIFont fontWithName:#"DBLCDTempBlack" size:11.2];
NSString * num = [NSString stringWithFormat:#"%4.3f",[gs getL]];
NSDictionary *textAttributes = #{NSFontAttributeName: font,
NSForegroundColorAttributeName: [UIColor whiteColor]};
CGSize textSize = [num sizeWithAttributes:textAttributes];
NSStringDrawingContext *drawingContext = [[NSStringDrawingContext alloc] init];
//adjust center
if (resizeRect.size.width - textSize.width > 0) {
resizeRect.origin.x += (resizeRect.size.width - textSize.width)/2;
}else{
resizeRect.origin.x -= (resizeRect.size.width - textSize.width)/2;
}
resizeRect.origin.y -= kTextPadding;
[num drawWithRect:resizeRect
options:NSStringDrawingUsesLineFragmentOrigin
attributes:textAttributes
context:drawingContext];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resizedImage;
}
Based on car type the annotations should change color.
Based on the title, the answer is NO :)
Normally you would dequeue the view and reset all relevant properties of the view
workaround:
What you can consider is: if the views would change too much, you could switch to a different reusingIdentifier, thereby switching the queue and 'circumventing' the cached views
Related
I am so confused with how to do what I need! Any help is very appreciated! I have an transparent image overlay that has another image behind it that has a UIPanGestureRecognizer and a UIPinchGestureRecognizer attached to it. The top image has a completely transparent middle that serves as the "glass" view. I am trying to crop the bottom image to the "glass" part based off pan and pinch. (see the image below to see what i'm talking about) I have successfully taken care of the pan crop but am having issues cropping it correctly when pinch is also applied.
I am not using snapshotViewAfterScreenUpdates.
Here is the code I have so far:
UIImage *snapshotImage;
/* draw the image of all the views below our view */
UIGraphicsBeginImageContextWithOptions(self.pendantImageView.bounds.size, NO, 0);
BOOL successfulDrawHierarchy = [self.pendantImageView drawViewHierarchyInRect:self.pendantImageView.bounds afterScreenUpdates:YES];
if ( successfulDrawHierarchy ) {
snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
} else {
NSLog(#"drawViewHierarchyInRect:afterScreenUpdates: failed - there's nothing to draw...");
}
UIGraphicsEndImageContext();
UIImage *croppedImage;
if ( successfulDrawHierarchy ) {
/* calculate the coordinates of the rectangle we're interested in within the returned image */
CGRect cropRect = CGRectOffset(pendantFrame, - self.pendantImageView.frame.origin.x, - self.pendantImageView.frame.origin.y);
/* draw the cropped section with a clipping region */
UIGraphicsBeginImageContextWithOptions(cropRect.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClipToRect(context, CGRectMake(0, 0, cropRect.size.width, cropRect.size.height));
CGRect targetRectangeForCrop = CGRectMake(-cropRect.origin.x, -cropRect.origin.y, snapshotImage.size.width, snapshotImage.size.height);
[snapshotImage drawInRect:targetRectangeForCrop];
croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
pendantImageView is the bottom imageView and pendantFrame is the middle coords of the area i'm trying to crop to.
Thanks in advance!
I am not sure whether I understood you correctly, but here is my result:
(click on the video to watch full version)
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIImageView *theImageView = [UIImageView new];
[self.view addSubview:theImageView];
theImageView.image = [UIImage imageNamed:#"image1.jpg"];
theImageView.frame = self.view.frame;
theImageView.userInteractionEnabled = YES;
theImageView.alpha = 0.6;
UIImageView *thePanImageView = [UIImageView new];
[self.view addSubview:thePanImageView];
thePanImageView.frame = CGRectMake(0, 0, 100, 100);
thePanImageView.center = CGPointMake(theImageView.frame.size.width/2, theImageView.frame.size.height/2);
thePanImageView.image = [self screenshotFromRect:thePanImageView.frame fromView:theImageView];
thePanImageView.userInteractionEnabled = YES;
thePanImageView.layer.cornerRadius = thePanImageView.frame.size.width/2;
thePanImageView.clipsToBounds = YES;
thePanImageView.theWeakObject = theImageView;
{
UIPanGestureRecognizer *thePanGesture = [UIPanGestureRecognizer new];
[thePanGesture addTarget:self action:#selector(handlePanGesture:)];
[thePanImageView addGestureRecognizer:thePanGesture];
UIPinchGestureRecognizer *thePinchGesture = [UIPinchGestureRecognizer new];
[thePinchGesture addTarget:self action:#selector(handlePinchGesture:)];
[thePanImageView addGestureRecognizer:thePinchGesture];
}
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)thePanGesture
{
UIImageView *thePanImageView = (id)thePanGesture.view;
UIImageView *theImageView = thePanImageView.theWeakObject;
thePanImageView.center = [thePanGesture locationInView:theImageView];
thePanImageView.image = [self screenshotFromRect:thePanImageView.frame fromView:theImageView];
}
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)thePinchGesture
{
UIImageView *thePanImageView = (id)thePinchGesture.view;
static CGRect theInitialFrame;
if (thePinchGesture.state == UIGestureRecognizerStateBegan)
{
theInitialFrame = thePanImageView.frame;
}
else
{
CGRect theFrame = theInitialFrame;
theFrame.size.width *= thePinchGesture.scale;
theFrame.size.height *= thePinchGesture.scale;
thePanImageView.frame = theFrame;
}
thePanImageView.layer.cornerRadius = thePanImageView.frame.size.width/2;
UIImageView *theImageView = thePanImageView.theWeakObject;
thePanImageView.center = [thePinchGesture locationInView:theImageView];
thePanImageView.image = [self screenshotFromRect:thePanImageView.frame fromView:theImageView];
}
- (UIImage * __nonnull)screenshotFromRect:(CGRect)theRect fromView:(UIView * __nonnull)theView;
{
if (!theView)
{
abort();
}
if (theRect.size.height < 1 || theRect.size.width < 1)
{
abort();
}
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)])
{
UIGraphicsBeginImageContextWithOptions(theRect.size, YES, [UIScreen mainScreen].scale);
}
else
{
UIGraphicsBeginImageContext(theRect.size);
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, -theRect.origin.x, -theRect.origin.y);
[theView.layer renderInContext:ctx];
UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshotImage;
}
EDIT:
The above solution is inefficient in terms of CPU, because it takes screenshots every time you move the view.
A much more efficient way would be to create an extra UIImageView, and simply move it inside of your thePanImageView
The code is the following:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIImageView *theImageView = [UIImageView new];
[self.view addSubview:theImageView];
theImageView.image = [UIImage imageNamed:#"image1.jpg"];
theImageView.frame = self.view.frame;
theImageView.userInteractionEnabled = YES;
theImageView.alpha = 0.6;
UIImageView *thePanImageView = [UIImageView new];
[self.view addSubview:thePanImageView];
thePanImageView.frame = CGRectMake(0, 0, 100, 100);
thePanImageView.center = CGPointMake(theImageView.frame.size.width/2, theImageView.frame.size.height/2);
thePanImageView.userInteractionEnabled = YES;
thePanImageView.layer.cornerRadius = thePanImageView.frame.size.width/2;
thePanImageView.clipsToBounds = YES;
thePanImageView.layer.borderColor = [UIColor redColor].CGColor;
thePanImageView.layer.borderWidth = 1;
thePanImageView.theWeakObject = theImageView;
{
UIPanGestureRecognizer *thePanGesture = [UIPanGestureRecognizer new];
[thePanGesture addTarget:self action:#selector(handlePanGesture:)];
[thePanImageView addGestureRecognizer:thePanGesture];
UIPinchGestureRecognizer *thePinchGesture = [UIPinchGestureRecognizer new];
[thePinchGesture addTarget:self action:#selector(handlePinchGesture:)];
[thePanImageView addGestureRecognizer:thePinchGesture];
UIImageView *theExtraImageView = [UIImageView new];
[thePanImageView addSubview:theExtraImageView];
theExtraImageView.frame = CGRectMake(-thePanImageView.frame.origin.x, -thePanImageView.frame.origin.y, theImageView.frame.size.width, theImageView.frame.size.height);
theExtraImageView.image = theImageView.image;
}
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)thePanGesture
{
UIImageView *thePanImageView = (id)thePanGesture.view;
UIImageView *theImageView = thePanImageView.theWeakObject;
thePanImageView.center = [thePanGesture locationInView:theImageView];
UIImageView *theExtraImageView = thePanImageView.subviews.firstObject;
theExtraImageView.frame = CGRectMake(-thePanImageView.frame.origin.x, -thePanImageView.frame.origin.y, theExtraImageView.frame.size.width, theExtraImageView.frame.size.height);
}
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)thePinchGesture
{
UIImageView *thePanImageView = (id)thePinchGesture.view;
static CGRect theInitialFrame;
if (thePinchGesture.state == UIGestureRecognizerStateBegan)
{
theInitialFrame = thePanImageView.frame;
}
else
{
CGRect theFrame = theInitialFrame;
theFrame.size.width *= thePinchGesture.scale;
theFrame.size.height *= thePinchGesture.scale;
thePanImageView.frame = theFrame;
}
thePanImageView.layer.cornerRadius = thePanImageView.frame.size.width/2;
UIImageView *theImageView = thePanImageView.theWeakObject;
thePanImageView.center = [thePinchGesture locationInView:theImageView];
UIImageView *theExtraImageView = thePanImageView.subviews.firstObject;
theExtraImageView.frame = CGRectMake(-thePanImageView.frame.origin.x, -thePanImageView.frame.origin.y, theExtraImageView.frame.size.width, theExtraImageView.frame.size.height);
}
FYI:
theWeakObject is just my custom property to NSObject. I used it because I was lazy and didn't want to create globally-visible #property
you should ask for how to output a image use mask! here is the link for the solution:
CGContextClipToMask returning blank image
you need to change:
CGContextClipToRect(context, CGRectMake(0, 0, cropRect.size.width, cropRect.size.height));
to something like:
CGContextClipToMask(context, CGRectMake(0, 0, cropRect.size.width, cropRect.size.height), maskImage);
I have a Custom UIView inside my prototype cell in a nd iOS 8.2 app before it was a regular UIImage and i was getting it back fine with [currentCell viewWithTag:200]; inside my UITableView cellForRowIndexPath method. But after i have made a custom uiview and replaced the uiimage it always returns nil..
edit
After trying to add a new label to a working menu controller that is way more simple just loops over a array of strings. and setting the tag and in the loop (that is already printing the correct value out for the menu) it can't find the newly added label. is it possible to somehow not get the tags compiled ?
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *currentCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Lists *listItem = [lists objectAtIndex:indexPath.row];
if(currentCell == nil){
currentCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//THIS ALLWAYS RETUNS nil
ShadedBadgeImage *cellImage = (ShadedBadgeImage *)[currentCell viewWithTag:200];
//UIView *cellImage = (UIView *)[currentCell viewWithTag:200];
//cellImage.completedProcentage = 2.0 * listItem.id.doubleValue;
UILabel *cellTitle = (UILabel *) [currentCell viewWithTag:101];
UILabel *cellDescription = (UILabel *)[currentCell viewWithTag:102];
cellTitle.text = listItem.id.stringValue;
cellDescription.text = listItem.shortText;
return currentCell;
And my custom uiview. that is using the layoutSubviews to render the masked image and progress bar.
IB_DESIGNABLE
#implementation ShadedBadgeImage {
double lineWidth;
CAShapeLayer *backgroundLayer;
CAShapeLayer *coloredLayer;
CALayer *imageLayer;
}
- (void)baseInit {
lineWidth = 5.0;
[self updateStroke];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self baseInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self baseInit];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
//Adds the background ring layer
if(backgroundLayer == nil)
{
backgroundLayer = [[CAShapeLayer alloc] init];
CGRect rect = CGRectInset(self.bounds, lineWidth/2.0, lineWidth/2.0);
UIBezierPath *roundPath = [UIBezierPath bezierPathWithOvalInRect:rect];
backgroundLayer.path = roundPath.CGPath;
backgroundLayer.fillColor = nil;
backgroundLayer.lineWidth = lineWidth;
backgroundLayer.strokeColor = [UIColor colorWithWhite:0.5 alpha:0.05].CGColor;
[self.layer addSublayer:backgroundLayer];
}
backgroundLayer.frame = self.layer.frame;
//Adds the color ring layer
if(coloredLayer == nil)
{
coloredLayer = [[CAShapeLayer alloc] init];
CGRect rect = CGRectInset(self.bounds, lineWidth/2.0, lineWidth/2.0);
UIBezierPath *roundPath = [UIBezierPath bezierPathWithOvalInRect:rect];
coloredLayer.path = roundPath.CGPath;
coloredLayer.fillColor = nil;
coloredLayer.lineWidth = lineWidth;
coloredLayer.strokeColor = [UIColor blueColor].CGColor;
coloredLayer.anchorPoint = CGPointMake(0.5, 0.5);
coloredLayer.transform = CATransform3DRotate(coloredLayer.transform, -M_PI/2, 0, 0, 1);
[self.layer addSublayer:coloredLayer];
}
coloredLayer.frame = self.layer.frame;
//Adds the color ring layer
if(imageLayer == nil)
{
CAShapeLayer *imageMask = [[CAShapeLayer alloc] init];
CGRect insertBounds = CGRectInset(self.bounds, lineWidth + 3.0, lineWidth + 3.0);
UIBezierPath *innerPath = [UIBezierPath bezierPathWithOvalInRect:insertBounds];
imageMask.path = innerPath.CGPath;
imageMask.fillColor = [UIColor grayColor].CGColor;
imageMask.frame = self.bounds;
[self.layer addSublayer:imageMask];
imageLayer = [[CALayer alloc] init];
imageLayer.mask = imageMask;
imageLayer.frame = self.layer.frame;
imageLayer.backgroundColor =[UIColor grayColor].CGColor;
imageLayer.contentsGravity = kCAGravityResizeAspectFill;
[self.layer addSublayer:imageLayer];
}
[self updateStroke];
}
- (void) updateStroke{
if (coloredLayer != nil)
{
coloredLayer.strokeEnd = self.completedProcentage;
}
if(self.image != nil)
{
imageLayer.contents = (__bridge id)([self.image CGImage]);
}
}
#end
As the comments says, dont use tags go for custom UITableCellViews for more control
I am trying to rotate an image that is added to MKMapView as an annotation.
This is the code:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation: (id<MKAnnotation>)annotation
{
if (! [annotation isKindOfClass:[IGAMapAnnotation class]])
{
//return default view if annotation is NOT of type IGAMapAnnotation...
return nil;
}
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"IGAMapAnnotation"];
if (annotationView == nil)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"IGAMapAnnotation"];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
else
{
annotationView.annotation = annotation;
}
IGAMapAnnotation *myLocation = (IGAMapAnnotation *) annotation;
// THIS IS IT!
if ([myLocation.type isEqual: #"PLANE"]) {
UIImage *planeImage = [UIImage imageNamed:#"planetracked.png"];
UIImageView *planeImageView = [[UIImageView alloc]initWithImage:planeImage];
planeImageView.transform = CGAffineTransformMakeRotation(M_PI_2);
annotationView.image = planeImageView;
}
return annotationView;
}
It obviously gives me an error as annotationView.image should assign an image and not UIImageView. I have tried various methods rotating just an image, for example this:
- (UIImage *)rotateImage:(UIImage *)image onDegrees:(NSString *)heading {
double angle = [heading doubleValue];
CGSize s = {image.size.width, image.size.height};
UIGraphicsBeginImageContext(s);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, 0,image.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextRotateCTM(ctx, 2*M_PI*angle/360);
CGContextDrawImage(ctx,CGRectMake(0,0,image.size.width, image.size.height),image.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
They do not work either -- no image appears on the map.
Anyone knows how to rotate an annotation image on MKMapView?
Million thanks!
Instead of:
annotationView.image = planeImageView;
which is definitely wrong (the image property is a UIImage while planeImageView is a UIImageView), use addSubview: to add the UIImageView to the annotation view (leaving the view's image property nil and unused).
However, you'll also need to make some other adjustments so that:
The image is centered exactly on the coordinate (instead of its corner), and
Tapping anywhere on the image brings up the callout (instead of only one specific corner)
To do these things, increase the frame sizes of both views to account for the maximum width possible from a rotation (which is the square root of 2 times the original width assuming image is a square) and set the image view's contentMode to "center" so the image is not distorted by these frame size changes.
The other big issue is that if you have IGAMapAnnotations whose type is not "PLANE", they will either be:
Invisible if a new annotation view is created (because image is not set nor is any subview added to the annotation view), or,
Showing a "plane" image with the heading of some other annotation because the annotation view was dequeued (and is being re-used for another annotation).
To avoid the two types of annotations ("plane"/"not plane") from re-using each other's views, I suggest using a different re-use identifier for each type (not each annotation) and apply type-specific changes to the view.
The revised viewForAnnotation method would look like this:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if (! [annotation isKindOfClass:[IGAMapAnnotation class]])
{
//return default view if annotation is NOT of type IGAMapAnnotation...
return nil;
}
IGAMapAnnotation *myLocation = (IGAMapAnnotation *)annotation;
BOOL typeIsPlane = [myLocation.type isEqualToString:#"PLANE"];
int planeImageViewTag = 42;
NSString *reuseId = typeIsPlane ? #"IGAMapAnnotationPlane" : #"IGAMapAnnotationOther";
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (annotationView == nil)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
if (typeIsPlane)
{
//Here, just add the image view to the annotation view with no
//rotation. Only want to add the image view to the annotation
//view ONCE when the annotation view is initially created. If
//view is dequeued, it will already have an image view and we
//just update its rotation.
UIImage *planeImage = [UIImage imageNamed:#"planetracked.png"];
UIImageView *planeImageView = [[UIImageView alloc] initWithImage:planeImage];
planeImageView.tag = planeImageViewTag;
planeImageView.contentMode = UIViewContentModeCenter;
[annotationView addSubview: planeImageView];
CGRect avFrame = annotationView.frame;
//"1.5" on next line is the square root of 2 rounded up a bit.
avFrame.size = CGSizeMake(planeImage.size.width*1.5,
planeImage.size.height*1.5);
annotationView.frame = avFrame;
planeImageView.frame = annotationView.frame;
}
else
{
//If this IGAMapAnnotation is not a "plane",
//show some other default image.
//(Or, you could return nil to show a default red pin.)
annotationView.image = [UIImage imageNamed:#"NotAPlane.png"];
//May or may not need to set centerOffset.
//Either remove or adjust 0,0 as needed to
//center the image on the coordinates.
annotationView.centerOffset = CGPointMake(0, 0);
}
}
else
{
annotationView.annotation = annotation;
}
//At this point, we have a new or dequeued annotation view ready
//and pointing to the current annotation.
//Now make any annotation-specific changes to the view...
if (typeIsPlane)
{
UIImageView *planeImageView = (UIImageView *)[annotationView viewWithTag:planeImageViewTag];
planeImageView.transform = CGAffineTransformMakeRotation(M_PI_2);
//Replace M_PI_2 with rotation specific to this annotation's heading.
}
return annotationView;
}
By the way, use isEqualToString: instead of isEqual: with NSStrings.
For the removeAnnotations: problem, it must be that mapLocations contains new instances of the annotations on the map. To remove existing annotations, you have to provide a reference to the exact same objects that were added originally.
If you are always removing all annotations and re-adding all annotations, you can just do [self.mapView removeAnnotations:self.mapView.annotations];.
If you are only removing some annotations, you'll need to keep references to the ones originally added or iterate through the map view's annotations array and identify which ones should be deleted (keep a temporary NSMutableArray as the list of "annotations to remove") and then call removeAnnotations: with that list of annotations to remove.
The following seems to work. Million thanks to Anna without whom it would not have!
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if (! [annotation isKindOfClass:[IGAMapAnnotation class]]) {
return nil;
}
IGAMapAnnotation *myLocation = (IGAMapAnnotation *) annotation;
BOOL typeIsPlane = [myLocation.navaidType isEqualToString:#"PLANE"];
BOOL typeIsOne = [myLocation.navaidType isEqualToString:#"ONE"];
BOOL typeIsTwo = [myLocation.navaidType isEqualToString:#"TWO"];
BOOL typeIsThree = [myLocation.navaidType isEqualToString:#"THREE"];
int planeImageViewTag = 42;
NSString *reuseId;
if (typeIsPlane)
reuseId = #"IGAMapAnnotationPlane";
else if (typeIsOne)
reuseId = #"IGAMapAnnotationOne";
else if (typeIsTwo)
reuseId = #"IGAMapAnnotationTwo";
else if (typeIsThree)
reuseId = #"IGAMapAnnotationThree";
else
reuseId = #"IGAMapAnnotationOther";
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (annotationView == nil)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
if (typeIsPlane)
{
UIImage *planeImage = [UIImage imageNamed:#"mapPLANE.png"];
UIImageView *planeImageView = [[UIImageView alloc] initWithImage:planeImage];
planeImageView.tag = planeImageViewTag;
planeImageView.contentMode = UIViewContentModeCenter;
[annotationView addSubview: planeImageView];
CGRect avFrame = annotationView.frame;
//"1.5" on next line is the square root of 2 rounded up a bit.
avFrame.size = CGSizeMake(planeImage.size.width*1.5,
planeImage.size.height*1.5);
annotationView.frame = avFrame;
planeImageView.frame = annotationView.frame;
}
else if (typeIsOne)
{
annotationView.image = [UIImage imageNamed:#"one.png"];
annotationView.centerOffset = CGPointMake(0, 0);
}
else if (typeIsTwo)
{
annotationView.image = [UIImage imageNamed:#"two.png"];
annotationView.centerOffset = CGPointMake(0, 0);
}
else if (typeIsThree)
{
annotationView.image = [UIImage imageNamed:#"three.png"];
annotationView.centerOffset = CGPointMake(0, 0);
}
else
return nil;
}
else
{
annotationView.annotation = annotation;
}
if (typeIsPlane)
{
// Convert current heading string to double
double headingDouble = [currentHeading doubleValue];
UIImageView *planeImageView = (UIImageView *)[annotationView viewWithTag:planeImageViewTag];
planeImageView.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(headingDouble));
}
return annotationView;
}
The annotationView bubble size is too small 9 out of 10 times.
I call [self.mapView selectAnnotation:self.selectedVenue animated:YES]; to show annotation view when select a row. Whether I set animated: YES or NO it still displays wrong size. However, if I increase size of map view height to at lease 200 pixels, everything looks fine except for the map view is too big for 3.5" screen.
I want the map view to be this size and annotation bubble to cover the title and subtitle correctly.
image: https://dl.dropboxusercontent.com/u/5105730/anno.png
Here's how I create my annotation view.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
if ([annotation isKindOfClass:[FSVenue class]]) {
static NSString *PlaceAnnotationIdentifier = #"Place Identifier";
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:PlaceAnnotationIdentifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:PlaceAnnotationIdentifier];
}
annotationView.annotation = annotation;
UIButton *calloutButton = [UIButton buttonWithType:(UIButtonTypeContactAdd)];
calloutButton.tintColor = self.themeColor;
annotationView.enabled = YES;
annotationView.pinColor = MKPinAnnotationColorGreen;
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = calloutButton;
annotationView.animatesDrop = YES;
return annotationView;
}
return nil;
}
You have to zoom the visible rect of your mapview after adding your annotations like this.
[self.yourMapview addAnnotations:self.yourAnnotationsArray];
[self zoomToAnnotations];
Call this method after adding your annotation like this
-(void)zoomToAnnotations{
MKMapRect zoomRect = MKMapRectNull;
for (_yourAnnotation in self.yourMapView.annotations) {
MKMapPoint annotationPoint = MKMapPointForCoordinate(_yourAnnotation.coordinate);
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
if (MKMapRectIsNull(zoomRect)) {
zoomRect = pointRect;
}else{
zoomRect = MKMapRectUnion(zoomRect, pointRect);
}
}
if (zoomRect.size.width == 0.10) /* for single annotation available in map */
{
zoomRect = MKMapRectMake(zoomRect.origin.x, zoomRect.origin.y, 100000, 100000);
}
[[self yourMapView] setVisibleMapRect:zoomRect edgePadding:UIEdgeInsetsMake(50, 50, 50, 50) animated:YES];
}
I am using Xcode 4.4 and target is iOS 5.
As on the title, I would like to create multiple pinch gestures for different images. I use two type of images. One is set as a background and the 2nd type of image is a stamp that is pasted on the image that is set as background. To do this I am using UIGestureRecognizer. Everything is fine until I paste the stamp type of image and try to zoom that image. I can zoom but when I zoom the image, the image has has been set as background zooms as well.
With the code below, I can paste different images (stamp type), tap and drag them individually but not when I try all the content zooms as well. Why isn't working?
Thank you in advance
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
NSLog(#"menuListType=%# ; menuID=%#", self.category.menuListType ,self.menu.menuID);
UIImage *aImage;
NSString *imagepath = [NSString stringWithFormat:#"%#%#",[FileUtility getPDFImageFolderPath], self.menu.imageID];
if (self.menu.imageID == nil || [self.menu.imageID isEqualToString:#""]) {
// self.canvasView.image = [UIImage imageNamed:#"NotExistFile.jpg"];
aImage = [UIImage imageNamed:#"NotExistFile.jpg"];
} else {
// self.canvasView.image = [UIImage imageWithContentsOfFile:imagepath];
aImage = [UIImage imageWithContentsOfFile:imagepath];
// self.canvasView = [[[UIImageView alloc] initWithImage:self.canvasView.image] autorelease];
}
// Choose image base and set as background
if ([self.category.menuListType isEqualToString:#"4"]) {
//set size
int imageW = aImage.size.width;
int imageH = aImage.size.height;
float scale = (imageW > imageH ? 500.0f/imageH : 500.0f/imageW);
CGSize resizedSize = CGSizeMake(imageW * scale, imageH * scale);
UIGraphicsBeginImageContext(resizedSize);
[aImage drawInRect:CGRectMake(0, 0, resizedSize.width, resizedSize.height)];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.canvasView.contentMode = UIViewContentModeTop;
self.canvasView.image = resizedImage;
if (self.menu.imageID == nil || [self.menu.imageID isEqualToString:#""]) {
} else {
self.canvasView = [[[UIImageView alloc] initWithImage:self.canvasView.image] autorelease];
}
} else {
// Paste image and set as background
UIImage *captureImage;
NSString *capturepath = [NSString stringWithFormat:#"%#%#",[FileUtility getPDFImageFolderPath], #"capture.png"];
if ([FileUtility fileExist:capturepath]) {
captureImage = [UIImage imageWithContentsOfFile:capturepath];
} else {
captureImage = [UIImage imageNamed:#"NotExistFile3.png"];
}
self.canvasView.contentMode = UIViewContentModeTop;;
self.canvasView.image = captureImage;
self.canvasView = [[[UIImageView alloc] initWithImage:self.canvasView.image] autorelease];
// Prepare Gestures
iViewsTapidx = 0;
iViewsDblTapidx = 0;
ivMax = sizeof(iViews) / sizeof(iViews[0]);
isTaped = NO;
isDblTaped = NO;
_width = 100;
// Initialization code
// Single tap
UITapGestureRecognizer *tapg =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(tapAction:)];
[self.itemView addGestureRecognizer:tapg];
// Double tap
UITapGestureRecognizer *doubleTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(doubleTapAction:)];
doubleTap.numberOfTapsRequired = 2;
[self.itemView addGestureRecognizer:doubleTap];
// longPress(長押し)
UILongPressGestureRecognizer *longPress =
[[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(longPressAction:)];
[self.itemView addGestureRecognizer:longPress];
// drag
UIPanGestureRecognizer *pan =
[[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(panAction:)];
[self.itemView addGestureRecognizer:pan];
//pinch
UIPinchGestureRecognizer *pinchGestureRognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinch:)];
[self.itemView addGestureRecognizer:pinchGestureRognizer];
// Stamp Image
int i = 0;
CGPoint tapPoint = CGPointMake(self.canvasView.center.x, self.canvasView.center.y);
int parcent = aImage.size.width / _width; // Prepare image to paste
// Show stamp
*(iViews+i) = [[UIImageView alloc] initWithImage:aImage];
(*(iViews+i)).frame = CGRectMake(tapPoint.x - aImage.size.width/parcent/2,
tapPoint.y - aImage.size.height/parcent/2,
aImage.size.width/parcent,
aImage.size.height/parcent);
(*(iViews+i)).tag = i+1;
[self.itemView addSubview:*(iViews+i)];
iViewsTapidx = i;
isTaped = YES;
self.itemView.userInteractionEnabled = YES;
}
[self showAnimation];
}
// tap
-(void)tapAction:(UITapGestureRecognizer *) sender{
CGPoint tapPoint = [sender locationInView:self.itemView];
NSLog(#">>>tap x=%.2f, y=%.2f", tapPoint.x, tapPoint.y);
int i =0;
isTaped = NO;
for (i = 0; i < ivMax; i++) {
if (CGRectContainsPoint((*(iViews+i)).frame, tapPoint)) {
isTaped = YES;
iViewsTapidx = i;
NSLog(#"i = %d", i);
break;
}
}
}
// doubleTap
-(void)doubleTapAction:(UITapGestureRecognizer *) sender{
NSLog(#">>>doubleTap");
CGPoint tapPoint = [sender locationInView:self.itemView];
isTaped = NO;
isDblTaped = NO;
int i =0;
for (i = 0; i < ivMax; i++) {
if (CGRectContainsPoint((*(iViews+i)).frame, tapPoint)) {
isDblTaped = YES;
iViewsDblTapidx = i;
break;
}
}
// remove
if (isDblTaped) {
NSLog(#"remove %d", i);
(*(iViews+i)).tag = 0;
[*(iViews+i) removeFromSuperview];
}
}
// longPress
- (void)longPressAction:(UILongPressGestureRecognizer *) sender{
if ([sender state] == UIGestureRecognizerStateBegan) {
NSLog(#">>>longPress 1");
}else if ([sender state] == UIGestureRecognizerStateEnded) {
CGPoint tapPoint = [sender locationInView:self.itemView];
NSLog(#">>>longPress 2 x=%.2f, y=%.2f", tapPoint.x, tapPoint.y);
int i =0;
for (i = 0; i < ivMax; i++) {
NSLog(#"i = %d", i);
if ((*(iViews+i)).tag == 0) {
break;
}
}
if (i < ivMax) {
//Stamp imapge
UIImage *stampImage;
NSString *imagepath = [NSString stringWithFormat:#"%#%#",[FileUtility getPDFImageFolderPath], self.menu.imageID];
if (self.menu.imageID == nil || [self.menu.imageID isEqualToString:#""]) {
stampImage = [UIImage imageNamed:#"NotExistFile.jpg"];
} else {
stampImage = [UIImage imageWithContentsOfFile:imagepath];
}
int parcent = stampImage.size.width / _width; // Set stamp image
//Sow stamp image
*(iViews+i) = [[UIImageView alloc] initWithImage:stampImage];
(*(iViews+i)).frame = CGRectMake(tapPoint.x - stampImage.size.width/parcent/2,
tapPoint.y - stampImage.size.height/parcent/2,
stampImage.size.width/parcent,
stampImage.size.height/parcent);
(*(iViews+i)).tag = i+1;
[self.itemView addSubview:*(iViews+i)];
iViewsTapidx = i;
isTaped = YES;
}
}
}
// drag
- (void)panAction:(UIPanGestureRecognizer *) sender{
NSLog(#">>>pan");
if (isTaped) {
CGPoint p = [sender translationInView:self.itemView];
CGPoint movePoint = CGPointMake((*(iViews+iViewsTapidx)).center.x + p.x,
(*(iViews+iViewsTapidx)).center.y + p.y);
(*(iViews+iViewsTapidx)).center = movePoint;
// NSLog(#">>>pan x=%.2f, y=%.2f --> x=%.2f, y=%.2f", p.x, p.y, movePoint.x, movePoint.y);
NSLog(#">>>pan x=%.2f, y=%.2f", p.x, p.y);
[sender setTranslation:CGPointZero inView:self.itemView];
}
}
-(void) handlePinch:(UIGestureRecognizer *) sender {
UIPinchGestureRecognizer *pinchGesture = (UIPinchGestureRecognizer *) sender;
if (pinchGesture.state == UIGestureRecognizerStateBegan || pinchGesture.state == UIGestureRecognizerStateChanged) {
//UIView *view = pinchGesture.view;
// UIView *view = self.itemView;
self.itemView.transform = CGAffineTransformScale(self.itemView.transform, pinchGesture.scale, pinchGesture.scale);
pinchGesture.scale = 1;
NSLog(#"zoom");
}
}
You will have to add Gesture Recognizer on Uiimageview, Look here for doing so : UIGestureRecognizer on UIImageView
Yet another way is to use UIScrolliew as your container instead of UIView and use the - (UIView*)viewForZoomingInScrollView:(UIScrollView *)aScrollView {
return imageView;
}
here imageView is the image you want to zoom,this will zoom that only image you are pinching its tested and works fine.