I am trying to add some kind of indicator for the location of the dragged MKAnnotationPin..
So far I came up with the following non-working solution:
- (void) showDragLoc{
UIView *cross = [[UIView alloc] initWithFrame:CGRectMake(dragPin.center.x, dragPin.center.y, 10, 10)];
[self.mapView addSubview:cross];
while(dragPin.dragState == MKAnnotationViewDragStateDragging){
[UIView beginAnimations:nil context:NULL]; // animate the following:
cross.frame = CGRectMake(dragPin.center.x, dragPin.center.y, 10, 10); // move to new location
[UIView setAnimationDuration:0.3];
[UIView commitAnimations];
}
}
ere dragPin is the MKAnnotationView that is declared in the header.. This function is called when the dragState of the pin goes to MKAnnotationViewDragStateDragging (from the delegate method)..
My goal is to add some kind of indicator of where the dragged pin currently is..
If you want to add a crosshair to the standard MKPinAnnotationView, you should subclass it and then add your crosshair in your implementation of setDragState:animated:.
So, to subclass, create a new class, e.g., PinWithCrosshairAnnotationView. The .h public interface doesn't need much:
#interface PinWithCrosshairAnnotationView : MKPinAnnotationView
#end
The .m implementation just implements a setDragState:animated: that adds your crosshair and then calls the super implementation to get the pin pulling and dropping features. I'm also animating that, if the animated flag is on, but you don't have to. Your frame coordinates will undoubtedly be different from mine, but I gather from your code sample above that you've already figured out the right values for your crosshair image:
#import "PinWithCrosshairAnnotationView.h"
#interface PinWithCrosshairAnnotationView ()
#property (nonatomic, weak) UIImageView *crosshairImageView;
#end
#implementation PinWithCrosshairAnnotationView
- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated
{
if (newDragState == MKAnnotationViewDragStateStarting)
{
// create the crosshair imageview and add it as a subview
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(-1.5, 30, 17.5, 17.5)];
imageView.image = [UIImage imageNamed:#"Crosshairs.png"];
[self addSubview:imageView];
// if the animated flag is on, we'll fade it to visible state
if (animated)
{
imageView.alpha = 0.0;
[UIView animateWithDuration:0.2
animations:^{
imageView.alpha = 1.0;
}];
}
// save a reference to that imageview in a class property
self.crosshairImageView = imageView;
}
else if (newDragState == MKAnnotationViewDragStateEnding || newDragState == MKAnnotationViewDragStateCanceling)
{
if (animated)
{
// if we're animating, let's quickly fade it to invisible
// and in the completion block, we'll remove it
[UIView animateWithDuration:0.2
animations:^{
self.crosshairImageView.alpha = 0.0;
}
completion:^(BOOL finished) {
[self.crosshairImageView removeFromSuperview];
self.crosshairImageView = nil;
}];
}
else
{
// if we're not animating, just remove it
[self.crosshairImageView removeFromSuperview];
self.crosshairImageView = nil;
}
}
// remember to call super so we get all the other wonderful superclass behavior
[super setDragState:newDragState animated:animated];
}
#end
And obviously, you'd then customize the viewForAnnotation in your MKMapView delegate accordingly. This is a minimalist version, but you'd obviously adjust yours depending upon your needs (callouts, title, subtitle, etc.):
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
MKAnnotationView *view = [[PinWithCrosshairAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:#"pinwithcrosshairannotation"];
view.draggable = YES;
view.canShowCallout = NO;
return view;
}
Related
i have a map view in my view controller, when i make a pin on the desired place i give my own image despite of the default image , but when i run and check the map it always shows default pin in my map instead of my image that i have passed. My code is this,
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
MKAnnotationView *view=[self.mapView dequeueReusableAnnotationViewWithIdentifier:#"annoView"];
if (!view) {
view=[[MKAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"annoView"];
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}else{
view.image=[UIImage imageNamed:#"car_marker.png"];
view.canShowCallout=YES;
}
}
return view;
}
MapPin *pin=[[MapPin alloc]init];
pin.title=loc[#"name"];
pin.subtitle=nil;
pin.coordinate=annoCordinate;
[self.mapView addAnnotation:pin];
[self.mapView setCenterCoordinate:pin.coordinate animated:YES];
This is very easy to accomplish, you just need to subclass MKAnnotationView and add a custom image view to it. A good example can be found here.
In case the link dies, I have also included the code snippet below, please do show the OP some love though!
#interface TStickerAnnotationView : MKAnnotationView
#property(nonatomic) float stickerColor;
#end
#interface TStickerAnnotationView () {
UIImageView *_imageView;
TCircleView *_circleView;
}
#end
#implementation TStickerAnnotationView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// make sure the x and y of the CGRect are half it's
// width and height, so the callout shows when user clicks
// in the middle of the image
CGRect viewRect = CGRectMake(-30, -30, 60, 60);
TCircleView* circleView = [[TCircleView alloc] initWithFrame:viewRect];
_circleView = circleView;
[self addSubview:circleView];
UIImageView* imageView = [[UIImageView alloc] initWithFrame:viewRect];
// keeps the image dimensions correct
// so if you have a rectangle image, it will show up as a rectangle,
// instead of being resized into a square
imageView.contentMode = UIViewContentModeScaleAspectFit;
_imageView = imageView;
[self addSubview:imageView];
}
return self;
}
- (void)setImage:(UIImage *)image
{
// when an image is set for the annotation view,
// it actually adds the image to the image view
_imageView.image = image;
}
- (void)stickerColor:(float)color {
_circleView.green = color;
[_circleView setNeedsDisplay];
}
How would one present a UIViewController (from Storyboard say) that is modal, and slides up from the bottom of the presenting view controller. Requirements would be:
slides up from bottom, with widths aligning with the width of the presenting view controller
does NOT take up whole screen or whole parent presenting view controller (rather only is as high as required to show itself)
can be shown within the context of a view controller which doesn't take the whole screen
I do not use storyboards so I wrote it all out. You can copy paste this into a brand new project and run it to see it working.
Your PresentingController needs to conform to two things. The first protocol is: UIViewControllerTransitioningDelegate which allows the controller to provide a custom presenter (namely itself in our case below). Whatever you return here (be it self, or some other object) needs to conform to UIViewControllerAnimatedTransitioning and provide the custom animations. For this self-contained example, I chose the current viewController to be the presenter and animator.
Next, it needs to conform to protocol: UIViewControllerAnimatedTransitioning which provides the custom animation for any presenting or dismissing controllers.
In other words, when we present or dismiss a viewController, animateTransition from the UIViewControllerAnimatedTransitioning protocol will be called to determine how the child controller should animate into perspective or dismiss from the view-port.
Example (With Transition Animation):
//
// ViewController.m
// SO
//
// Created by Brandon T on 2017-01-23.
// Copyright © 2017 XIO. All rights reserved.
//
#import "ViewController.h"
//Some view controller that will be presented modally.
//I have coloured it red.
#interface ModalController : UIViewController
#end
#implementation ModalController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
}
#end
//The view controller that will present or dismiss some other view controller modally.
//I have coloured it white.
#interface ViewController () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning>
#property (nonatomic, assign) bool presentingModalController;
#property (nonnull, nonatomic, strong) ModalController *modalController;
#property (nonnull, nonatomic, strong) UIButton *button;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
//For this example, I add a button to present and dismiss the redViewController.
self.button = [[UIButton alloc] initWithFrame:CGRectMake(15, self.view.center.y - 100, self.view.frame.size.width - 30, 45)];
[self.button setTitle:#"Present" forState:UIControlStateNormal];
[self.button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.button setBackgroundColor:[UIColor lightGrayColor]];
[self.button.layer setCornerRadius:5.0];
[self.button addTarget:self action:#selector(onButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.button];
//Create the redViewController and set its transitioning delegate to self (this controller will be providing the animation and presenter).
//We also set the style to OverFullScreen because we don't want this controller to disappear.
//When a view controller is presented, the one that presented it usually disappears or gets removed from the hierarchy until the child is dismissed. In the case of alerts, or controllers that need to display OVER the current one, we need to set the modalPresentationStyle.
self.modalController = [[ModalController alloc] init];
self.modalController.transitioningDelegate = self;
self.modalController.modalPresentationStyle = UIModalPresentationOverFullScreen;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)onButtonClicked:(UIButton *)button {
if (self.modalController.view.window == nil) {
[self presentViewController:self.modalController animated:YES completion:nil];
[self.button setTitle:#"Dismiss" forState:UIControlStateNormal];
//not a good idea but meh.. I need to keep this example short.
[self.view.window addSubview:self.button];
}
else {
[self.modalController dismissViewControllerAnimated:YES completion:nil];
[self.button setTitle:#"Present" forState:UIControlStateNormal];
[self.view addSubview:self.button];
}
}
//Custom Animations and Presenters.
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
self.presentingModalController = true; //We are presenting the controller.
return self; //Who is animating it? We are.
}
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
self.presentingModalController = false; //We are dismissing the view controller.
return self; //Who animated it? We did.
}
//How fast should it present? I chose 0.5 seconds.
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
return 0.5;
}
//The actual animation code.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
if (self.presentingModalController) {
//If we are presenting, we need to add the new controller's view as a sub-view.
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//We need a starting frame for the animation.
CGRect startingFrame = transitionContext.containerView.bounds;
startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
startingFrame.size.height = 100; //Has a height of 100.
//We need an end frame for the animation.
CGRect finalFrame = transitionContext.containerView.bounds;
finalFrame.origin.y = finalFrame.size.height - 100; //100 from the bottom of the parent.
finalFrame.size.height = 100; //Present with a size of 100 height.
//Add the controller's view as a subview of the context.
[transitionContext.containerView addSubview:toViewController.view];
[toViewController.view setFrame:startingFrame];
//Start animating from "startFrame" --> "endFrame" with 0.5 seconds duration and no delay. I chose easeIn style.
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[toViewController.view setFrame:finalFrame];
} completion:^(BOOL finished) {
//We are finished animating, complete the transition!
[transitionContext completeTransition:YES];
}];
}
else {
//If we are dismissing the view controller, we need to animate it down the screen and then remove its view from the context.
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//We only need one frame. This is the first frame. We are animating from "endFrame" --> "startingFrame" (backwards/reverse animation).
CGRect startingFrame = transitionContext.containerView.bounds;
startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
startingFrame.size.height = 100; //Has a height of 100.
//Start the animation with 0.5 seconds duration and I chose easeOut style.
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[fromViewController.view setFrame:startingFrame];
} completion:^(BOOL finished) {
//Remove the view controller's view from the context and complete the transition!
[fromViewController.view removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}
}
#end
Example (Without Transition Animation):
//
// ViewController.m
// SO2
//
// Created by Brandon Thomas on 2017-01-23.
// Copyright © 2017 XIO. All rights reserved.
//
#import "ViewController.h"
#interface ModalController : UIViewController
#end
#implementation ModalController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
}
#end
#interface ViewController ()
#property (nonatomic, assign) bool presentingModalController;
#property (nonnull, nonatomic, strong) ModalController *modalController;
#property (nonnull, nonatomic, strong) UIButton *button;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.button = [[UIButton alloc] initWithFrame:CGRectMake(15, self.view.center.y - 100, self.view.frame.size.width - 30, 45)];
[self.button setTitle:#"Present" forState:UIControlStateNormal];
[self.button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.button setBackgroundColor:[UIColor lightGrayColor]];
[self.button.layer setCornerRadius:5.0];
[self.button addTarget:self action:#selector(onButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.button];
self.modalController = [[ModalController alloc] init];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)onButtonClicked:(UIButton *)button {
if (self.modalController.view.window == nil) {
//Present
CGRect startingFrame = self.view.bounds;
startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
startingFrame.size.height = 100; //Has a height of 100.
CGRect finalFrame = self.view.bounds;
finalFrame.origin.y = finalFrame.size.height - 100; //100 from the bottom of the parent.
finalFrame.size.height = 100; //Present with a size of 100 height.
[self.modalController.view setFrame:startingFrame];
[self.modalController willMoveToParentViewController:self];
[self addChildViewController:self.modalController];
[self.view addSubview:self.modalController.view];
[self.modalController didMoveToParentViewController:self];
[UIView animateWithDuration:0.5 animations:^{
[self.modalController.view setFrame:finalFrame];
} completion:^(BOOL finished) {
}];
}
else {
//Dismiss
CGRect startingFrame = self.view.bounds;
startingFrame.origin.y = startingFrame.size.height; //Starts from the bottom of the parent.
startingFrame.size.height = 100; //Has a height of 100.
[UIView animateWithDuration:0.5 animations:^{
[self.modalController.view setFrame:startingFrame];
} completion:^(BOOL finished) {
[self.modalController.view removeFromSuperview];
[self.modalController willMoveToParentViewController:nil];
[self.modalController removeFromParentViewController];
[self.modalController didMoveToParentViewController:nil];
}];
}
}
#end
Check out the Apple documentation for this:
Presenting a View Controller Using Custom Animations
To present a view controller using custom animations, do the following
in an action method of your existing view controllers:
Create the view controller that you want to present. Create your
custom transitioning delegate object and assign it to the view
controller’s transitioningDelegate property. The methods of your
transitioning delegate should create and return your custom animator
objects when asked. Call the
presentViewController:animated:completion: method to present the view
controller. When you call the
presentViewController:animated:completion: method, UIKit initiates the
presentation process. Presentations start during the next run loop
iteration and continue until your custom animator calls the
completeTransition: method. Interactive transitions allow you to
process touch events while the transition is ongoing, but
noninteractive transitions run for the duration specified by the
animator object.
EDIT:
Your alternative option is to create a container with your custom sizes and animate your UIViewController added as a child view of your UIViewController:
[self addChildViewController:content];
content.view.frame = [self frameForContentController];
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self];
Taken from this Thread
I want to show ContainerView's view controller as like this
I use the following Code and it shows as i want
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.8];
if (_addLinkQuestionView.isHidden == YES)
{
_addLinkQuestionView.hidden = NO;
_addLinkQuestionView.alpha = 1.0;
}
else
{
_addLinkQuestionView.alpha = 0.0;
_addLinkQuestionView.hidden = YES;
}
[UIView commitAnimations];
but Upon a click on blurr area, i want to hide container view. that area is the UIButton. I use the following code, but it does nothing.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.8];
_addLinkQuestionView.alpha = 0.0;
_addLinkQuestionView.hidden = YES;
[UIView commitAnimations];
Any Help. Thanx in advance.
Essentially it seems you need to show a alertview behaviour where all the ui from app is disabled while only the content in the dialog is enabled.
Add a public method like showOverlayView:(UIView*)v to your app delegate
On this method create a view, set alpha and add it to keywindow.
Now add the passed view to keywindow and calculate and set
its center property.
Alternatively you could use a library like MJPopupViewController or SLPopupViewController to do the job for you.
The correct way to do this :
1- New File -> UIView -> rename it to addLinkQuestionView
2- New File -> obj c class -> rename it to addLinkQuestionView
Now u have a xib,a .h and a .m
3- Go to the xib and in file's owner select your addLinkQuestionView u created in step 2
4- Design the xib as the picture link you posted and link up appropriate outlets to addLinkQuestionView.h
5- For initialization of your uiview in .h do this :
#import "addLinkQuestionView.h"
#implementation addLinkQuestionView
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code.
[[NSBundle mainBundle] loadNibNamed:#"addLinkQuestionView" owner:self options:nil];
self.vuComplete.frame = CGRectMake(self.vuComplete.frame.origin.x, self.vuComplete.frame.origin.y, self.frame.size.width, self.frame.size.height);
[self addSubview:self.vuComplete];
self.vuContainer.layer.cornerRadius = 5.0;
self.vuContainer.layer.borderWidth = 1.0/[UIScreen mainScreen].scale;
self.vuContainer.layer.borderColor = [[UIColor clearColor]CGColor];
self.vuContainer.alpha = 0.0;
[self layoutIfNeeded];
}
return self;
}
-(void)awakeFromNib
{
}
- (IBAction)onBackgroundTapDismissView:(id)sender {
[UIView animateWithDuration:0.5
animations:^{self.vuContainer.alpha = 0.0;}
completion:^(BOOL finished){ }];
[self removeFromSuperview];
}
Note : - (IBAction)onBackgroundTapDismissView can be a accomplished by dropping a uitapgesturerecognizer on your addLinkQuestionView's greyed background uiview so that tapping it can dismiss the entire uiview (vuComplete)
6- Then add this in your main view controller that is presenting this pop up like this :
A- import addLinkQuestionView.h first
B- add this code on your button action that you're clicking for presenting addLinkQuestionView:
addLinkQuestionView *popup = [[addLinkQuestionView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[UIView animateWithDuration:0.25
animations:^{popup. addLinkQuestionView.alpha = 1.0;}
completion:^(BOOL finished){ }];
[self.view addSubview:popup];
Have fun!
I have made an animation that moves across the screen, my animation loops continuously. How can I stop the animation when you tap the animated image, Then let the animation continue when you lift the touch?
I know how to use TouchesMoved to move a specified button like this:
CGPoint point = [[[event allTouches] anyObject] locationInView:self.view];
UIControl *control = sender;
control.center = point;
But getting it to work with my animation. I would like the animation to continue after I touch it.
SelectedCellViewController.h
// SelectedCellViewController.h
#import <Accounts/Accounts.h>
#import <QuartzCore/QuartzCore.h>
#interface SelectedCellViewController : UIViewController {
IBOutlet UIImageView *imageView;
UIImageView *rocket;
}
#end
viewControllertoShow.m
#import "SelectedCellViewController.h"
#interface SelectedCellViewController ()
#end
#implementation SelectedCellViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
}
return self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(imageSpawn:) withObject:nil afterDelay:3];
}
- (void) imageSpawn:(id) sender
{
UIImage* image = [UIImage imageNamed:#"ae"];
rocket = [[UIImageView alloc] initWithImage:image];
rocket.frame = CGRectMake(-25, 200, 25, 40);
[UIView animateWithDuration:5
delay:0.2f
options:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse
animations:^(){rocket.frame=CGRectMake(345, 200, 25, 40);}
completion:^(BOOL fin) {
}];
[self.view addSubview:rocket];
UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(ballTapped:)];
tapped.numberOfTapsRequired = 1;
[rocket addGestureRecognizer:tapped];
[rocket setUserInteractionEnabled:YES];
}
-(void)ballTapped:(UIGestureRecognizer *)gesture
{
CGPoint location = [gesture locationInView:gesture.view];
//then write code to remove the animation
[self.view.layer removeAllAnimations];
NSLog(#"Tag = %d", gesture.view.tag);
rocket.frame = CGRectMake(location.x,location.y,25,40);
}
- (void)dismissView {
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)viewDidUnload {
}
#end
As you had already stated in your question ,you can get the touched point using
CGPoint point = [[[event allTouches] anyObject] locationInView:self.view];
Then check check whether this point lies inside the coordinates of the animated UIImageview and then stop animation.
But if you are using scrollview, you won't be able to use this because scrollview will not return any UIView touch events.
As your image view is animating, a better choice will be adding a UITapGestureRecogniser to the image view when you add it s subview, like this
- (void) imageSpawn:(id) sender
{
UIImage* image = [UIImage imageNamed:#"ae"];
UIImageView *rocket = [[UIImageView alloc] initWithImage:image];
rocket.frame = CGRectMake(-25, 200, 25, 40);
[UIView animateWithDuration:5
delay:0.2f
options:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse
animations:^(){rocket.frame=CGRectMake(345, 200, 25, 40);}
completion:^(BOOL fin) {
}];
[myScrollView addSubview:rocket];
UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(ballTapped:)];
tapped.numberOfTapsRequired = 1;
[self.rocket addGestureRecognizer:tapped];
[rocket setUserInteractionEnabled:YES];
}
And in the target function write code to stop the animation:
-(void)ballTapped:(UIGestureRecognizer *)gesture
{
//here also you can get the tapped point if you need
CGPoint location = [gesture locationInView:gesture.view];
//then write code to remove the animation
[self.view.layer removeAllAnimations];
}
EDIT:
If you are trying to stop the image view at the touched point, you can add this to ballTapped event:
rocket.frame = CGRectMake(location.x,location.y,25,40);
But for this you have to declare UIImageView *rocket outside this particular method, i.e. declare to in your header file.
Another way is to add this to the parent view of the animation -
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
self.animating = NO;
return [super hitTest:point withEvent:event];
}
use an (atomic) property, then check the property in the animation to see if it should be stopped. We use this to stop a photo gallery which is running so that the user can manually move the photos. You could also check in here if the point is in a specific area if necessary. This method runs before any touch methods are called.
try like this may be it helps you, in the middle of the animation if you touch on the imageview then animation removes from the view .
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint lastLocation = [touch locationInView: self.view];
if([[touch view] isKindOfClass:[UIImageView class]]){
[youImageView.layer removeAllAnimations];
youImageView.center=lastLocation;//assign your image view center when the animation removes from the view.
}
}
and add #import <QuartzCore/QuartzCore.h> framework.
I'm currently working with the mapkit and am stuck.
I have a custom annotation view I am using, and I want to use the image property to display the point on the map with my own icon. I have this working fine. But what I would also like to do is to override the default callout view (the bubble that shows up with the title/subtitle when the annotation icon is touched). I want to be able to control the callout itself: the mapkit only provides access to the left and right ancillary callout views, but no way to provide a custom view for the callout bubble, or to give it zero size, or anything else.
My idea was to override selectAnnotation/deselectAnnotation in my MKMapViewDelegate, and then draw my own custom view by making a call to my custom annotation view. This works, but only when canShowCallout is set to YES in my custom annotation view class. These methods are NOT called if I have this set to NO (which is what I want, so that the default callout bubble is not drawn). So I have no way of knowing if the user touched on my point on the map (selected it) or touched a point that is not part of my annotation views (delected it) without having the default callout bubble view show up.
I tried going down a different path and just handling all touch events myself in the map, and I can't seem to get this working. I read other posts related to catching touch events in the map view, but they aren't exactly what I want. Is there a way to dig into the map view to remove the callout bubble before drawing? I'm at a loss.
Any suggestions? Am I missing something obvious?
There is an even easier solution.
Create a custom UIView (for your callout).
Then create a subclass of MKAnnotationView and override setSelected as follows:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected)
{
//Add your custom view to self...
}
else
{
//Remove your custom view...
}
}
Boom, job done.
detailCalloutAccessoryView
In the olden days this was a pain, but Apple has solved it, just check the docs on MKAnnotationView
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.detailCalloutAccessoryView = UIImageView(image: UIImage(named: "zebra"))
Really, that's it. Takes any UIView.
Continuing on from #TappCandy's brilliantly simple answer, if you want to animate your bubble in the same way as the default one, I've produced this animation method:
- (void)animateIn
{
float myBubbleWidth = 247;
float myBubbleHeight = 59;
calloutView.frame = CGRectMake(-myBubbleWidth*0.005+8, -myBubbleHeight*0.01-2, myBubbleWidth*0.01, myBubbleHeight*0.01);
[self addSubview:calloutView];
[UIView animateWithDuration:0.12 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^(void) {
calloutView.frame = CGRectMake(-myBubbleWidth*0.55+8, -myBubbleHeight*1.1-2, myBubbleWidth*1.1, myBubbleHeight*1.1);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 animations:^(void) {
calloutView.frame = CGRectMake(-myBubbleWidth*0.475+8, -myBubbleHeight*0.95-2, myBubbleWidth*0.95, myBubbleHeight*0.95);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.075 animations:^(void) {
calloutView.frame = CGRectMake(-round(myBubbleWidth/2-8), -myBubbleHeight-2, myBubbleWidth, myBubbleHeight);
}];
}];
}];
}
It looks fairly complicated, but as long as the point of your callout bubble is designed to be centre-bottom, you should just be able to replace myBubbleWidth and myBubbleHeight with your own size for it to work. And remember to make sure your subviews have their autoResizeMask property set to 63 (i.e. "all") so that they scale correctly in the animation.
:-Joe
Found this to be the best solution for me.
You'll have to use some creativity to do your own customizations
In your MKAnnotationView subclass, you can use
- (void)didAddSubview:(UIView *)subview{
int image = 0;
int labelcount = 0;
if ([[[subview class] description] isEqualToString:#"UICalloutView"]) {
for (UIView *subsubView in subview.subviews) {
if ([subsubView class] == [UIImageView class]) {
UIImageView *imageView = ((UIImageView *)subsubView);
switch (image) {
case 0:
[imageView setImage:[UIImage imageNamed:#"map_left"]];
break;
case 1:
[imageView setImage:[UIImage imageNamed:#"map_right"]];
break;
case 3:
[imageView setImage:[UIImage imageNamed:#"map_arrow"]];
break;
default:
[imageView setImage:[UIImage imageNamed:#"map_mid"]];
break;
}
image++;
}else if ([subsubView class] == [UILabel class]) {
UILabel *labelView = ((UILabel *)subsubView);
switch (labelcount) {
case 0:
labelView.textColor = [UIColor blackColor];
break;
case 1:
labelView.textColor = [UIColor lightGrayColor];
break;
default:
break;
}
labelView.shadowOffset = CGSizeMake(0, 0);
[labelView sizeToFit];
labelcount++;
}
}
}
}
And if the subview is a UICalloutView, then you can screw around with it, and what's inside it.
I had the same problem. There is a serious of blog posts about this topic on this blog http://spitzkoff.com/craig/?p=81.
Just using the MKMapViewDelegate doesn't help you here and subclassing MKMapView and trying to extend the existing functionality also didn't work for me.
What I ended up doing is to create my own CustomCalloutView that I am having on top of my MKMapView. You can style this view in any way you want.
My CustomCalloutView has a method similar to this one:
- (void) openForAnnotation: (id)anAnnotation
{
self.annotation = anAnnotation;
// remove from view
[self removeFromSuperview];
titleLabel.text = self.annotation.title;
[self updateSubviews];
[self updateSpeechBubble];
[self.mapView addSubview: self];
}
It takes an MKAnnotation object and sets its own title, afterward it calls two other methods which are quite ugly which adjust the width and size of the callout contents and afterward draw the speech bubble around it at the correct position.
Finally the view is added as a subview to the mapView. The problem with this solution is that it is hard to keep the callout at the correct position when the map view is scrolled. I am just hiding the callout in the map views delegate method on a region change to solve this problem.
It took some time to solve all those problems, but now the callout almost behaves like the official one, but I have it in my own style.
Basically to solve this, one needs to:
a) Prevent the default callout bubble from coming up.
b) Figure out which annotation was clicked.
I was able to achieve these by:
a) setting canShowCallout to NO
b) subclassing, MKPinAnnotationView and overriding the touchesBegan and touchesEnd methods.
Note: You need to handle the touch events for the MKAnnotationView and not MKMapView
I just come up with an approach, the idea here is
// Detect the touch point of the AnnotationView ( i mean the red or green pin )
// Based on that draw a UIView and add it to subview.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
CGPoint newPoint = [self.mapView convertCoordinate:selectedCoordinate toPointToView:self.view];
// NSLog(#"regionWillChangeAnimated newPoint %f,%f",newPoint.x,newPoint.y);
[testview setCenter:CGPointMake(newPoint.x+5,newPoint.y-((testview.frame.size.height/2)+35))];
[testview setHidden:YES];
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
CGPoint newPoint = [self.mapView convertCoordinate:selectedCoordinate toPointToView:self.view];
// NSLog(#"regionDidChangeAnimated newPoint %f,%f",newPoint.x,newPoint.y);
[testview setCenter:CGPointMake(newPoint.x,newPoint.y-((testview.frame.size.height/2)+35))];
[testview setHidden:NO];
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
NSLog(#"Select");
showCallout = YES;
CGPoint point = [self.mapView convertPoint:view.frame.origin fromView:view.superview];
[testview setHidden:NO];
[testview setCenter:CGPointMake(point.x+5,point.y-(testview.frame.size.height/2))];
selectedCoordinate = view.annotation.coordinate;
[self animateIn];
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
NSLog(#"deSelect");
if(!showCallout)
{
[testview setHidden:YES];
}
}
Here - testview is a UIView of size 320x100 - showCallout is BOOL - [self animateIn]; is the function that does view animation like UIAlertView.
You can use leftCalloutView, setting annotation.text to #" "
Please find below the example code:
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if(pinView == nil){
pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];
}
CGSize sizeText = [annotation.title sizeWithFont:[UIFont fontWithName:#"HelveticaNeue" size:12] constrainedToSize:CGSizeMake(150, CGRectGetHeight(pinView.frame)) lineBreakMode:UILineBreakModeTailTruncation];
pinView.canShowCallout = YES;
UILabel *lblTitolo = [[UILabel alloc] initWithFrame:CGRectMake(2,2,150,sizeText.height)];
lblTitolo.text = [NSString stringWithString:ann.title];
lblTitolo.font = [UIFont fontWithName:#"HelveticaNeue" size:12];
lblTitolo.lineBreakMode = UILineBreakModeTailTruncation;
lblTitolo.numberOfLines = 0;
pinView.leftCalloutAccessoryView = lblTitolo;
[lblTitolo release];
annotation.title = #" ";
I've pushed out my fork of the excellent SMCalloutView that solves the issue with providing a custom view for callouts and allowing flexible widths/heights pretty painlessly. Still some quirks to work out, but it's pretty functional so far:
https://github.com/u10int/calloutview