iOS6 mapkit span - mapkit

The new maps provided with iOS6 do not allow a span lower than about .001.
Has anyone else seen this issue?
CLLocationCoordinate2D coord = {.latitude= 37.980600, .longitude= -87.593932};
MKCoordinateSpan span = {.latitudeDelta= 0.0, .longitudeDelta = 0.0}; //smaller the number = greater zoom
MKCoordinateRegion region = {coord, span};
[mapView setRegion:region animated:YES];
Even using 0.0 as the zoom span, I can open the map in the simulator and zoom further with a gesture.
Also worth noting is that I can zoom further in the actual 'Maps' App using gestures, than I can in my own app.

Yes, it works exactly as you describe, and I sent off some sample code to Apple to see if they had anything to say about it. No answer yet. It must be intentional, pe

Related

iPhone MKMapView: set span/region value to show all pins on map

I'm working on a project (platform iOS 7) in which i required current location with stores around 5km, so how to calculate the span/region value to display all stores with current location on map.
MKMapRect zoomRect = MKMapRectNull;
double inset;
for (id <MKAnnotation> annotation in mapVW.annotations)
{
MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
zoomRect = MKMapRectUnion(zoomRect, pointRect);
inset = -zoomRect.size.width * 20;
}
[mapVW setVisibleMapRect:MKMapRectInset(zoomRect, inset, inset) animated:YES];
this is what I'm trying
Thanks
It's not clear what your exact issue is but the following may help:
The calculation of the inset looks wrong. It's setting the inset (padding on the sides) to 20 times the width of the whole zoom area. What you probably want is to set the inset to a small fraction of the entire width. Maybe you meant 0.20 instead of 20.0:
inset = -zoomRect.size.width * 0.20;
You also don't need to repeatedly set the inset inside the for loop since it only depends on the final width. You can move the above line after the for loop before calling setVisibleMapRect.
You mention some issue with the current location. It's not clear what the issue is but maybe you mean that this zooming code doesn't include the current location? If so, maybe the current location hasn't been determined yet when this code is called. Try moving this code to (or also call it from) the didUpdateUserLocation delegate method. Make sure showsUserLocation is YES and that the map view's delegate is set.
By the way: iOS 7 includes the new method showAnnotations:animated: which automatically determines the bounding rectangle for some given annotations and sets the map's visible region for you. It doesn't let you specify a custom inset like you are doing (though the default isn't bad). So instead of the above loop, you would do:
[mapVW showAnnotations:mapVW.annotations animated:YES];
NSArray *anno_Arrr = mapview.annotations;
[mapview showAnnotations:anno_Arrr animated:YES];

MKMapView in reverse, when compass mode using in my iphone app project

I am developing an iphone application,in that app i am showing the client Parking places in different locations on MKMapView. Now I need to use compass mode in MKMapView to show the client parking places in particular direction (SE,SW,NE,NW), for that I run this below code.
-(void)updateHeading:(CLHeading *) newHeading
{
NSLog(#"New magnetic heading: %f", newHeading.magneticHeading);
NSLog(#"New true heading: %f", newHeading.trueHeading);
double rotation = newHeading.magneticHeading * 3.14159/-180;
[mapView setTransform:CGAffineTransformMakeRotation(-rotation)];
[[mapView annotations] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
MKAnnotationView * view = [mapView viewForAnnotation:obj];
[view setTransform:CGAffineTransformMakeRotation(rotation)];
}];
}
Everthing works fine in MKMapView, but my MKMapView shows in reverse order, when the device starts rotating, I am facing this problem in ios5 and ios6 also.
NOTE: when I test this app in America, map shows correctly, while I test the app in my location (India) Map turns into reverse.
`
Thanks in advance for any help.
Is the map intended to be centered on the user while rotating? If so you could just set the MKMapView's userTrackingMode to MKUserTrackingModeFollowWithHeading.
If not then how about telling us what you see in magneticHeading, trueHeading and rotation when things go right and when they go wrong.

How to flip a UIView around the x-axis while simultaneously switching subviews

This question has been asked before but in a slightly different way and I was unable to get any of the answers to work the way I wanted, so I am hoping somebody with great Core Animation skills can help me out.
I have a set of cards on a table. As the user swipes up or down the set of cards move up and down the table. There are 4 cards visible on the screen at any given time, but only the second card is showing its face. As the user swipes the second card flips back onto its face and the next card (depending on the swipe direction) lands in it's place showing its face.
I have set up my card view class like this:
#interface WLCard : UIView {
UIView *_frontView;
UIView *_backView;
BOOL flipped;
}
And I have tried flipping the card using this piece of code:
- (void) flipCard {
[self.flipTimer invalidate];
if (flipped){
return;
}
id animationsBlock = ^{
self.backView.alpha = 1.0f;
self.frontView.alpha = 0.0f;
[self bringSubviewToFront:self.frontView];
flipped = YES;
CALayer *layer = self.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, M_PI, 1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
};
[UIView animateWithDuration:0.25
delay:0.0
options: UIViewAnimationCurveEaseInOut
animations:animationsBlock
completion:nil];
}
This code works but it has the following problems with it that I can't seem to figure out:
Only half of the card across the x-axis is animated.
Once flipped, the face of the card is upside down and mirrored.
Once I've flipped the card I cannot get the animation to ever run again. In other words, I can run the animation block as many times as I want, but only the first time will animate. The subsequent times I try to animate lead to just a fade in and out between the subviews.
Also, bear in mind that I need to be able to interact with the face of the card. i.e. it has buttons on it.
If anybody has run into these issues it would be great to see your solutions. Even better would be to add a perspective transform to the animation to give it that extra bit of realism.
This turned out to be way simpler than I thought and I didn't have to use any CoreAnimation libraries to achieve the effect. Thanks to #Aaron Hayman for the clue. I used transitionWithView:duration:options:animations:completion
My implementation inside the container view:
[UIView transitionWithView:self
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromBottom
animations: ^{
[self.backView removeFromSuperview];
[self addSubview:self.frontView];
}
completion:NULL];
The trick was the UIViewAnimationOptionTransitionFlipFromBottom option. Incidentally, Apple has this exact bit of code in their documentation. You can also add other animations to the block like resizing and moving.
Ok, this won't be a complete solution but I'll point out some things that might be helpful. I'm not a Core-Animation guru but I have done a few 3D rotations in my program.
First, there is no 'back' to a view. So if you rotate something by M_PI (180 degrees) you're going to be looking at that view as though from the back (which is why it's upside down/mirrored).
I'm not sure what you mean by:
Only half of the card across the x-axis is animated.
But, it it might help to consider your anchor point (the point at which the rotation occurs). It's usually in the center, but often you need it to be otherwise. Note that anchor points are expressed as a proportion (percentage / 100)...so the values are 0 - 1.0f. You only need to set it once (unless you need it to change). Here's how you access the anchor point:
layer.anchorPoint = CGPointMake(0.5f, 0.5f) //This is center
The reason the animation only ever runs once is because transforms are absolute, not cumulative. Consider that you're always starting with the identity transform and then modifying that, and it'll make sense...but basically, no animation occurs because there's nothing to animate the second time (the view is already in the state you're requesting it to be in).
If you're animating from one view to another (and you can't use [UIView transitionWithView:duration:options:animations:completion:];) you'l have to use a two-stage animation. In the first stage of the animation, for the 'card' that is being flipped to backside, you'll rotate the view-to-disappear 'up/down/whatever' to M_PI_2 (at which point it will be 'gone', or not visible, because of it's rotation). And in the second stage, you're rotate the backside-of-view-to-disappear to 0 (which should be the identity transform...aka, the view's normal state). In addition, you'll have to do the exact opposite for the 'card' that is appearing (to frontside). You can do this by implementing another [UIView animateWithDuration:...] in the completion block of the first one. I'll warn you though, doing this can get a little bit complicated. Especially since you're wanting views to have a 'backside', which will basically require animating 4 views (the view-to-disappear, the view-to-appear, backside-of-view-to-disappear, and the backside-of-view-to-appear). Finally, in the completion block of the second animation you can do some cleanup (reset view that are rotated and make their alpha 0.0f, etc...).
I know this is complicated, so you might want read some tutorial on Core-Animation.
#Aaron has some good info that you should read.
The simplest solution is to use a CATransformLayer that will allow you to place other CALayer's inside and maintain their 3D hierarchy.
For example to create a "Card" that has a front and back you could do something like this:
CATransformLayer *cardContainer = [CATransformLayer layer];
cardContainer.frame = // some frame;
CALayer *cardFront = [CALayer layer];
cardFront.frame = cardContainer.bounds;
cardFront.zPosition = 2; // Higher than the zPosition of the back of the card
cardFront.contents = (id)[UIImage imageNamed:#"cardFront"].CGImage;
[cardContainer addSublayer:cardFront];
CALayer *cardBack = [CALayer layer];
cardBack.frame = cardContainer.bounds;
cardBack.zPosition = 1;
cardBack.contents = (id)[UIImage imageNamed:#"cardBack"].CGImage; // You may need to mirror this image
[cardContainer addSublayer:cardBack];
With this you can now apply your transform to cardContainer and have a flipping card.
#Paul.s
I followed your approach with card container but when i applt the rotation animation on card container only one half of the first card rotates around itself and finally the whole view appears.Each time one side is missing in the animation
Based on Paul.s this is updated for Swift 3 and will flip a card diagonally:
func createLayers(){
transformationLayer = CATransformLayer(layer: CALayer())
transformationLayer.frame = CGRect(x: 15, y: 100, width: view.frame.width - 30, height: view.frame.width - 30)
let black = CALayer()
black.zPosition = 2
black.frame = transformationLayer.bounds
black.backgroundColor = UIColor.black.cgColor
transformationLayer.addSublayer(black)
let blue = CALayer()
blue.frame = transformationLayer.bounds
blue.zPosition = 1
blue.backgroundColor = UIColor.blue.cgColor
transformationLayer.addSublayer(blue)
let tgr = UITapGestureRecognizer(target: self, action: #selector(recTap))
view.addGestureRecognizer(tgr)
view.layer.addSublayer(transformationLayer)
}
Animate a full 360 but since the layers have different zPositions the different 'sides' of the layers will show
func recTap(){
let animation = CABasicAnimation(keyPath: "transform")
animation.delegate = self
animation.duration = 2.0
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.toValue = NSValue(caTransform3D: CATransform3DMakeRotation(CGFloat(Float.pi), 1, -1, 0))
transformationLayer.add(animation, forKey: "arbitrarykey")
}

How to constrain UIScrollView to only zoom vertically?

I have a UIScrollView which I'm using to represent an axis on a graph. I'd like the user to be able to zoom in on the axis using the usual pinch motion, but for it to only scale in the vertical direction, not horizontally.
My question is similar to this one, but I've tried the solution suggested there (overriding the subview's SetTransform method so that it ignores scaling in one direction) and it works perfectly when constraining scaling horizontally, but not vertically. When I try implementing it vertically the first pinch action works fine, but subsequent pinches seem to reset the zoom scale to one before having any effect.
Does anyone know what might be causing this behaviour, and more importantly how I can get around it?
I'm using MonoTouch but answers using Objective-C are fine.
I know this question was posted quite a while ago, but here's an answer for anyone stuck on this problem.
I looked over the question you linked to, rankAmateur, and I think the simple way to fix the solution found there to suit your needs is to replace the CGAffineTransform's "a" property with its "d" property in the setTransform: method.
- (void)setTransform:(CGAffineTransform)newValue;
{
CGAffineTransform constrainedTransform = CGAffineTransformIdentity;
// constrainedTransform.a = newValue.a;
constrainedTransform.d = newValue.d;
[super setTransform:constrainedTransform];
}
I'm not very well versed in CGAffineTransorm, but this worked for me and after browsing the documentation it seems the "a" property corresponds to a view's x-axis and the "d" property corresponds to a view's y-axis.
EDIT
So after going back and realizing what the question really was, I did some more digging into this and I'm a bit stumped, but having experienced the same behavior that rankAmateur mentions above, it seems incredibly unusual for the CGAffineTransform to work perfectly well with zoomScale when zooming is constrained to only horizontally, but not when constrained to only vertically.
The only hypothesis I can offer, is that it might have something to do with the differing default coordinate systems of Core Graphics and UIKit, since in those coordinate systems the x-axis functions in the same way, while the y-axis functions oppositely. Perhaps somehow this gets muddled up in the previously mentioned overriding of setTransform.
This answer depends heavily on the answer from starryVere (thumbs up!)
this is starryVere's code in Swift. It is in the zoomed UIView subclass:
var initialScale: CGFloat = 1.0
override var transform: CGAffineTransform {
set{
//print("1 transform... \(newValue), frame=\(self.frame), bounds=\(self.bounds)")
var constrainedTransform = CGAffineTransformIdentity
constrainedTransform.d = self.initialScale * newValue.d // vertical zoom
//constrainedTransform.a = newValue.a // horizontal zoom
super.transform = constrainedTransform
//print("2 transform... \(constrainedTransform), frame=\(self.frame), bounds=\(self.bounds)")
}
get{
return super.transform
}
}
The commented out prints are very helpful to understand what happens with bounds and frame during the transformation.
Now to the scale problem:
the method scrollViewDidEndZooming of the containing UIScrollViewDelegate has a parameter scale. According to my tests this parameter scale contains the value zoomedView.transform.a which is the horizontal scale factor that we set to 1.0 using CGAffineTransformIdentity. So scale is always 1.0.
The fix is easy:
func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) {
let myScale = zoomView.transform.d
}
use myScale like you would use scale in cases with horizontal zoom.
After struggling with the same issue, I was able to come up with a workaround.
Use this code for the setTransform method.
-(void)setTransform:(CGAffineTransform)transform
{
CGAffineTransform constrainedTransform = CGAffineTransformIdentity;
constrainedTransform.d = self.initialScale * transform.d;
constrainedTransform.d = (constrainedTransform.d < MINIMUM_ZOOM_SCALE) ? MINIMUM_ZOOM_SCALE : constrainedTransform.d;
[super setTransform:constrainedTransform];
}
Set the initialScale property from within the scrollViewWillBeginZooming delegate method.
It will be more helpful if you provide a sample code of what you are trying, but i am giving you some lines to try. Actually you have to make the content size width equal to "320" i.e. equal to the the screen size of iPhone.
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 45,320,480)];
scrollView.contentSize = CGSizeMake(320,1000);
scrollView.showsVerticalScrollIndicator = YES;
The MonoTouch version follows:
scrollView = new UIScrollView (new RectangleF (0, 45, 320, 480)) {
ContentSize = new SizeF (320, 1000),
ShowVerticalScrollIndicator = true
};
Hope it helps.. :)
and Yes dont forget to accept the answer if it helps :D

UIAccessoryElement, accessoryFrame and screen rotation broken

I have a UIControl subclass which follows the UIAccessibilityContainer informal protocol: it returns NO to -isAccessibilityElement, delivers the correct -accessibilityElementCount and elements in the accessors.
Each UIAccessibilityElement which is created to represent an accessibility region is created successfully, and the frame is a 1:1 mapping of another CGRect I'm drawing.
E.g., I'm drawing into {94, 99}, {209, 350}} and the -accessibilityFrame on the UIAccessibilityElement is set to the same CGRect value.
However, when in landscape (or upside-down portrait) orientation, the frames (only for accessibility elements, drawing still works fine) are rotated incorrectly. The top-left point relative to the frame is always the corner top-left of the home button.
Here's a screenshot from the simulator:
As you can see, it's in landscape mode, and the frame is totally impossibly not what it's specifying.
Here's the code driving the creation of the elements:
CGRect localRect = someCGRectVariable;
CGRect globalRect = CGRectOffset(localRect, CGRectGetMinX(self.accessibilityFrame), CGRectGetMinY(self.accessibilityFrame));
UIAccessibilityElement *accElem = [[UIAccessibilityElement alloc]initWithAccessibilityContainer:self];
accElem.isAccessibilityElement = YES;
accElem.accessibilityFrame = globalRect;
accElem.accessibilityHint = [NSString stringWithFormat:NSLocalizedString(#"xyz %#", nil), someName];
accElem.accessibilityTraits = UIAccessibilityTraitButton;
accElem.accessibilityLabel = nameValue;
It looks to me like the rotation is busted, but I can't put my finger on it. It's worth noting that it works perfectly fine in portrait mode.
accessibilityFrame returns its answer in screen coordinates, without adjusting for device rotation.
Somewhere in Apple's docs it suggests you use [UIWindow convertRect:toWindow:] in this sort of case.