I am trying to get a popover to appear at a map kit annotation point but cannot find a "rect" in the annotation view properties to use the rect method of calling uipopovercontroller. If given an annotation on map kit how does one find the appropriate "frame"?
To give paul more information, here is my attempt: I have already used:
- (void)mapView:(MKMapView *)mapView2 annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
NSLog(#"annotationView...");
MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
int choice = 0;
for (NSMutableDictionary *locationDictionary in [myGizmoClass searchResultsForResortLocation])
{
if([view.annotation.title isEqualToString:[locationDictionary objectForKey:#"name"]])
{
DetailViewTableStyleController *controller = [[DetailViewTableStyleController alloc] initWithlocationData:[[myGizmoClass searchResultsForResortLocation] objectAtIndex:choice] nibName:#"DetailViewTableStyle" bundle:[NSBundle mainBundle]];
controller.categoryCode = [locationDictionary objectForKey:#"category_code"] ;
//create a popover controller
popoverControllerDetail = [[UIPopoverController alloc] initWithContentViewController:controller];
// set contentsize
[popoverControllerDetail setPopoverContentSize:CGSizeMake(320,480)];
//present the popover view non-modal
[popoverControllerDetail presentPopoverFromRect:view.rightCalloutAccessoryView.frame inView:mapView2 permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[controller release];
break;
}
choice = choice + 1;
}
}
And... I get a popover at the upper left at the edge of the mapview.
Can anyone tell me why? I am trying to get it to appear near the pin/annotationview.
Okay,
Here is what I found as the only work-around so far:
CGPoint annotationPoint = [mapView2 convertCoordinate:view.annotation.coordinate toPointToView:mapView2];
float boxDY=annotationPoint.y;
float boxDX=annotationPoint.x;
CGRect box = CGRectMake(boxDX,boxDY,5,5);
UILabel *displayLabel = [[UILabel alloc] initWithFrame:box];
[popoverControllerDetail presentPopoverFromRect:displayLabel.frame inView:mapView2 permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[displayLabel release];
The bad part about this is that I am faking putting it at the view of the annotation. I simply get the annotation.coordinate of the annotation view (MKMapKit style coordinates) and convert the coordinates to screen coordinates. Then I create a uilabel (to get a frame-capable construct on the screen) placed where the Annotation View would be. I then tell the popovercontroller to appear at that frame location.
Here is the kicker. I do not add the uilabel to the mapView2. I simply release it once popovercontroller has drawn itself.
Hack, but clean.
The problem with the first example in this thread is in the call to presentPopoverFromRect:inView:permittedArrowDirections:animated:. The value for the inView: parameter is not correct. It should be view.rightCalloutAccessoryView.
That is, the rectangle's coordinates are relative to rightCalloutAccessoryView.
With this change, the behavior will be similar to the maps application which seems right.
If you have the MKAnnotationView, and you imply that you do, then you can use its frame property, as it is a UIView subclass.
Didn't read all the replies... but I had a similar problem. I wanted to show a popover box when I tap the accessory button in the callout bubble.
This is how I solved my problem.
First of, I center the map to the annotation I chose, which I think is always a good idea in this case. Then I implement the popover with a fixed size and position it at the correct position (in my opinion).
Here is a part of my code:
-(void)mapView:(MKMapView *)theMapView annotationView:(MKAnnotationView *)pin calloutAccessoryControlTapped:(UIControl *)control
{
UIViewController *detailController = [[detailedPinView alloc] initWithNibName:#"detailedPinView"
bundle:nil
annotationView:pin];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
UIPopoverController* aPopover = [[UIPopoverController alloc] initWithContentViewController:detailController];
[aPopover setDelegate:self];
[aPopover setPopoverContentSize:CGSizeMake(320, 320) animated:YES];
[detailController setPopover:aPopover];
[detailController release];
[mapView deselectAnnotation:pin.annotation animated:YES];
self.popoverController = aPopover;
[mapView setCenterCoordinate:pin.annotation.coordinate animated:YES];
[self.popoverController presentPopoverFromRect:CGRectMake(382,498,0,0) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
else
{
[self presentModalViewController: detailController animated:YES];
}
[detailController release];
}
The issue with presentPopoverFromRect:inView:permittedArrowDirections:animated is that if the content size of the popover is greater than what fits on the screen (because the annotation is close the the edge of the screen, for example), the system will first (at least partially) disregard the rectangle, and put the tip of the arrow somewhere in the middle of the view. I've solved this by doing it this way:
self.popover = [[UIPopoverController alloc] initWithContentViewController:placeDetailViewController];
[self.popover presentPopoverFromRect:CGRectMake(0, 0, 29, 31) inView:view.rightCalloutAccessoryView permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
The rightCalloutAccessoryView is normally a detail disclosure button, which has a size of 29 by 31. So, what I do, is create a 29 by 31 rectangle, which covers the entire accessory view. Then I present the popover from the accessory view. By increasing the 29 to 40, the popover arrow will be outside the callout bubble, but only if there is enough space to present the entire callout on the screen. If not, the arrow will be moved inside the bubble. To maintain a consistent interface, I left the width at 29.
Related
I want to add working UISliders as a subview to the map view of the DJI-iOS-UXSDK-demo (https://github.com/DJI-Mobile-SDK-Tutorials/iOS-UXSDKDemo). By "working" I just mean they should be freely draggable, right now they can only be moved in tiny steps to the right.
I tried adding the sliders using a .xib-View and just adding them programmatically. Both work fine for a generic MKMapView but not for the map view implemented in DJI-iOS-UXSDK-demo. I tried adjusting the frame sizes of the map view, the sliders and the .xib-view, but none of these changed anything for me.
To reproduce, please clone https://github.com/DJI-Mobile-SDK-Tutorials/iOS-UXSDKDemo and install the pods. Then add the following code to viewDidLoad in DefaultLayoutViewController.m after [super viewDidLoad]:
//Get the map view instance and switch it into the main view
MKMapView *mapView;
if ([self.previewViewController respondsToSelector: #selector(mapWidget)]) {
mapView = [[self.previewViewController valueForKey: #"mapWidget"] mapView];
[self performSelector:#selector(previewViewTapped:) withObject:nil];
};
//Add the slider
UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(25, 50, 200, 10)];
[mapView addSubview:slider];
You can ignore the registration error, as fixing that didn't change anything for me. The slider appears on the map view as expected, however it can only be dragged in small steps to the right. Adding a listener with addTarget:action:forControlEvents: also worked for me, however it is useless as long as the slider can't be dragged freely.
I followed your reproduction steps, and was able to reproduce and also fix the issue.
I simply added the slider as a subview of the DefaultLayoutViewController.m's view instead of a subview of the MKMapView and it starting to work smoothly.
//Get the map view instance and switch it into the main view
MKMapView *mapView;
if ([self.previewViewController respondsToSelector: #selector(mapWidget)]) {
mapView = [[self.previewViewController valueForKey: #"mapWidget"] mapView];
[self performSelector:#selector(previewViewTapped:) withObject:nil];
};
//Add the slider
UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(25, 50, 200, 10)];
[self.view addSubview:slider]; // <------ Add as a subview of the view controller and not the mapView
I worked around it by adding my own UIPanGestureRecognizer to the slider with the target
- (void)sliderMoved:(UIPanGestureRecognizer *)recognizer {
if([recognizer.view isKindOfClass:[UISlider class]]) {
UISlider *slider = recognizer.view;
slider.value = (slider.maximumValue - slider.minimumValue) * [recognizer locationInView:recognizer.view].x / recognizer.view.frame.size.width + slider.minimumValue;
//call the listener or just place your code here
[self sliderChanged:slider];
}
}
Doesn't solve the underlying problem, but this is what works best for me. I would still be interested in finding a fitting delegate for when the views change places if someone reading this happpens to know it.
I am segueing to a view controller called SmallerVC. I want to set the size of SmallerVC to 300 X 300. So far I am not having much luck. I have tried the answers at the following link separately and then together, they don't work: ios5 - size of modal view controller with storyboard. I am using Xcode-5 and iOS-7. Has anyone successfully done this? I came to iOS with the prejudice that anything I can do on Android I should be able to do more easily on iOS. And so far iOS has been constantly disabusing me of that notion. Can such a simple thing be really so difficult?
To get to the button of things, I set my content view's color to clearColor. And sure enough, during transition it is clear, but upon transitioning a black background shows up that covers the whole screen. So my guess is I need to access and manipulate some sort of parent. Any ideas how I might do this?
UPDATE: my code
Here is the latest iteration of my code. The modal segue is set in storyboard.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"smaller_view_id"]) {
if ([segue.destinationViewController isKindOfClass:[SmallerVC class]]) {
UIViewController *container = (SmallerVC *)segue.destinationViewController;
container.modalPresentationStyle = UIModalPresentationFormSheet;
id sourceController = segue.sourceViewController;
UIView *destinationView = [container view];
CGFloat x, y, w, h;
x = 10;//destinationView.superview.frame.origin.x;
y = 50;//destinationView.superview.frame.origin.y;
w = 300; // Your desired modal view width
h = 300; // Your desired modal view height
destinationView.superview.frame = CGRectMake(x, y, w, h);
destinationView.frame = CGRectMake(x, y, w, h);
destinationView.superview.center = [[sourceController view] center];
[container setModalPresentationStyle:UIModalPresentationFormSheet];
[container setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
// [sourceController presentViewController:container animated:YES completion:nil];
NSLog(#"SEGUE CALL IS SEEN");
#pragma warning - pass in previous review data
}
}
}
I've two buttons on iPad that opens two popups. They are opened with the code:
CGRect frame = [self.view convertRect:self.addNewFolderButton.frame fromView:self.addNewFolderButton.superview];
[self.popover presentPopoverFromRect:frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
and
CGRect frame = [self.view convertRect:self.addNewUserButton.frame fromView:self.addNewUserButton.superview];
[self.popover presentPopoverFromRect:frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
but my popView isn't opened in the same kind of way (the link with the calling button in positioned in different way. Why?
Because you are giving the arrow direction UIPopoverArrowDirectionAny which mean it could be any, set the specific direction for homogeneous behaviour
So I've tried this-
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
[self.mapAnnotationViewController.view removeFromSuperview];
MyLocation* location = (MyLocation*)view.annotation;
currentResultDictionary = [location cardJson];
[self.mapAnnotationViewController setAnnotationTitle: [location title]];
[self.mapAnnotationViewController setRating:3.0];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage* forwardButtonImage = [UIImage imageNamed:#"forward-50x50.png"];
button.frame = CGRectMake(141,5,25,25);
[button setBackgroundImage:forwardButtonImage forState:UIControlStateNormal];
[button addTarget:self action:#selector(displayCard:) forControlEvents:UIControlEventTouchUpInside];
//Since we are re-using the callout view,
//may need to do additional "cleanup" so that the callout
//shows the new annotation's data.
[view addSubview:self.mapAnnotationViewController.view];
[view addSubview: button];
}
-(IBAction)displayCard:(id)sender{
NSLog(#"DISPLAY CARD CALLED");
}
This adds the button successfully, but when I click on it, the displayCard method is not being called. Why is that happening?
So this is probably why it's not working -
How To add custom View in map's Annotation's Callout's
However, that solution subclasses the view - I only have access to the controller. Can I reduce my problem to be solved by that solution?
Based on your previous question, the problem here is that the button's frame is outside the frame of the pin annotation view. Therefore, touches on the button do nothing.
Assuming you are still using the default MKPinAnnotationView, the default view size is about 32 x 32. Since the button is being added as a subview to it at an x coordinate of 141, it is outside the parent view's frame and touches don't work.
One solution (though it leads to other issues) is to modify the MKPinAnnotationView's frame so that the button will included. So in viewForAnnotation, after the view is created, you could put:
pav = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"pin"];
pav.canShowCallout = NO;
//increase the frame size to include the button...
pav.frame = CGRectMake(0, 0, 200, 40);
//adjust contentMode otherwise default pin image will be distorted...
pav.contentMode = UIViewContentModeTopLeft;
I don't recommend the above. This may work but may lead to other issues.
A better solution probably is to add your custom callout view not to the annotation view but to your main view controller's view. This way, you don't need to mess with the annotation view. You'll need to convert the selected pin's coordinate to the corresponding CGPoint in the main view and set the custom callout view's origin to that point. In didSelectAnnotationView:
[self.mapAnnotationViewController.view removeFromSuperview];
CGRect mavcFrame = mapAnnotationViewController.view.frame;
CGPoint p = [mapView convertCoordinate:view.annotation.coordinate toPointToView:self.view];
//You may need/want to adjust p after the conversion depending on where
//you want the callout view to appear relative to the annotation.
mavcFrame.origin = p;
mapAnnotationViewController.view.frame = mavcFrame;
//add subview to self.view instead of view...
[self.view addSubview:self.mapAnnotationViewController.view];
Also, I don't recommend adding the button in the didSelectAnnotationView as you will end up with duplicate buttons that don't get removed every time an annotation is selected.
Instead, create and add the button once to the mapAnnotationViewController.view right after the mapAnnotationViewController is created.
I display a popover in map view as follows:
But when i rotate the device, It shows Like:
as you see it covers the annotation instead of showing just beside the annotation.
My code is as as shown below:
CGPoint annotationPoint = [mapView convertCoordinate:aView.annotation.coordinate toPointToView:mapView];
float boxDY=annotationPoint.y;
float boxDX=annotationPoint.x;
CGRect box = CGRectMake(boxDX,boxDY,5,5);
UILabel *displayLabel = [[UILabel alloc] initWithFrame:box];
[popView presentPopoverFromRect:displayLabel.frame inView:mapView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[displayLabel release];
Help me to solve this problem.
You can try to override the following method (declared in UIViewcontroller.h) :
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
You'll probably need to save the state of your viewController (like isPopoverPresented, which annotation etc.) and update the popover's rect accordingly.