PopOver frame issue in iPhone SDK - ios

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.

Related

How to add working sliders to map view in DJI-UXSDK-demo

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.

Need to add a Fixed Overlay like on mapview in IOS

I need to create a map view interface, which is something similar to the OLA Cabs Application in iOS. What I exactly wanna do is to fix an overlay on mapView and allow the user to scroll the map view across it. So that the overlay can be fixed at any location the User wants it to, I searched a lot about overlays, in iOS and MapKit, but couldn't make it possible. If some one can give me tips for achieving this I would be really grateful. Here is a snapshot of the screen
Here the annotation remains fixed and you can move the map view across it, So that when you stop the mapview, the overlay will be pointing to the new location, where you stopped
Click here to download demo...
Create a fix MKAnnotation and image view object to animating the location change effect in Map view.
#property (nonatomic, strong) CustomAnnotation *fixAnnotation;
#property (nonatomic, strong) UIImageView *annotationImage;
Add this code in viewDidLoad() method:
// Fix annotation
_fixAnnotation = [[CustomAnnotation alloc] initWithTitle:#"Fix annotation" subTitle:#"Location" detailURL:nil location:self.mapView.userLocation.coordinate];
[self.mapView addAnnotation:self.fixAnnotation];
// Annotation image.
CGFloat width = 64;
CGFloat height = 64;
CGFloat margiX = self.mapView.center.x - (width / 2);
CGFloat margiY = self.mapView.center.y - (height / 2) - 32;
// 32 is half size for navigationbar and status bar height to set exact location for image.
_annotationImage = [[UIImageView alloc] initWithFrame:CGRectMake(margiX, margiY, width, height)];
[self.annotationImage setImage:[UIImage imageNamed:#"mapannotation.png"]];
Now have to remove image when you drag a map view and add image which looks like an annotation. And after completion of that add annotation and remove image from Map View.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
NSLog(#"Region will changed...");
[self.mapView removeAnnotation:self.fixAnnotation];
[self.mapView addSubview:self.annotationImage];
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
NSLog(#"Region did changed...");
[self.annotationImage removeFromSuperview];
CLLocationCoordinate2D centre = [mapView centerCoordinate];
self.fixAnnotation.coordinate = centre;
[self.mapView addAnnotation:self.fixAnnotation];
}
Its not an map annotation overlay, its a normal UIImageView which has been placed over MKMapView, and it always used to get the lat-long for the center point of the map.
Hope this would be an easy way to achieve your goal.
#Kampai has added the same code for you.

Button not working in custom callout

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.

Placing a UIImageView on screen, issue with conversion of UIView coordinates

I have problems getting the coordinate convertion from the local view frame to the window right. I have an "Arrow" image that needs to be moved relative to a UIButton on the page when tapped and when the page loads.
The Arrow image (intense name "interfaceApertureSelectionArrow") is initialized in the "didLoadView" method
- (void)viewDidLoad
{
...
//only initial values here, the image i will be moved using the moveImageView:to: function
CGRect apertureSelectorArrowFrame = CGRectMake(300.0f, 125.0f, 30.0f, 30.0f);
[self addImageView:interfaceApertureSelectionLineArrow withFrame:apertureSelectorArrowFrame];
interfaceApertureSelectionLineArrow = [[UIImageView alloc] initWithFrame:apertureSelectorArrowFrame];
[interfaceApertureSelectionLineArrow setImage:[UIImage imageNamed:#"InterfaceSelectionLineArrow#2x"]];
interfaceApertureSelectionLineArrow.opaque = YES;
[self.view addSubview:interfaceApertureSelectionLineArrow];
...
}
For reacting on the button tap I implemented a Button Action, doing the coordinate conversion using the "convertPoint" method. In this case the conversion and arrow placement works works just right (!)
-(IBAction)setAttributeAperture:(id)sender{
UIButton *button = (UIButton *)sender;
CGPoint superPoint = [button.superview convertPoint:button.frame.origin toView:nil];
[self moveImageView:interfaceApertureSelectionLineArrow toCGPoint:CGPointMake(superPoint.x, superPoint.y)];
}
... so far so good. Now here's the problem. When I try to position the arrow next to the "darkButton" coordinates in the viewDidLoad method to show the selection not only on tapping the button but also on loading the view initially the positioning does not work! Here's what I tried.
Added to the viewDidLoad method:
...
CGPoint superPoint = [darkButton.superview convertPoint:darkButton.frame.origin toView:nil];
[self moveImageView:interfaceApertureSelectionLineArrow toCGPoint:CGPointMake(superPoint.x, superPoint.y)];
...
The move function that animates the movement of the arrow's UIImageView (in case this is relevant as well)
-(void)moveImageView:(UIImageView*)thisImageView
toCGPoint:(CGPoint)thisCGPoint{
[UIView beginAnimations:#"UIImage Move" context:NULL];
[UIView setAnimationDuration:1];
CGSize size = thisImageView.frame.size;
thisImageView.frame = CGRectMake(thisCGPoint.x, thisCGPoint.y, size.width, size.height);
[UIView commitAnimations];
}
Checking the CGPoint value before and after the conversion of the darkButton's CGPoint coordinates shows me that is does not change. How come? (as it did work in the "setAttributeAperture" button method)

Placing arrow of uipopovercontroller at annotation point on mapkit

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.

Resources