Might be a good idea to post the working code for those people that stumble over this thread looking for a solution.
Great idea. Here's what I came up with.
Since my custom map is embedded in a scrollview, my first instinct was to embed the map icons (pins, etc) in the scrollview as well and just change their size whenever the user zoomed in or out. That actually did work just fine, but there were some issues with it's position on the map whenever this occurred, its position would warp slightly.
So instead, I created a second UIView (not embedded in the scrollview) and put all the non-zoomable map elements on that. Whenever the user zooms now, I have a custom method that triggers a displayLink timer. The displayLink timer is similar to NSTimer, except it fires about 60 times per second (coordinated with the refresh rate of the display). Whenever this timer fires, it calls a custom method that uses Core Animation to move the custom map elements to wherever their correct position is on the map.
I find this correct position by using the UIView/UIScrollView method
convertPoint:toView: method. Since my application's display coordinate x,y system judges everything based on where it is in the actual map image, I first convert all points from that to the scrollview, then I convert it from the scrollview to self.view so that I can get their actual x,y position on the display. All of my non-zoomable map elements reside on a UIView with the same frame as self.view so it works.
Since some of the UIScrollView delegate methods will get called 10-20x when a user zooms in or zooms out, and I only want my displayLink to get created one time when they zoom in, I have to create a BOOL property called scrollIsActive to check and see if the UIScrollView methods have already triggered the displayLink timer.
I came to programming back in May and most of what I know is self-taught so if anyone can see any improvements that can be made I would be very grateful.
Here is the code:
ScrollView Delegate Methods that Trigger the Display Link Timer:
Code:
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (self.scrollIsActive == NO) {
NSLog(@"DID BEGIN DRAGGING");
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(zoomDragDisplayLinkTriggeredForDetails)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
self.scrollIsActive = YES;
}
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
NSLog(@"DID END DRAGGING");
[self.displayLink invalidate];
self.scrollIsActive = NO;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (self.scrollIsActive == NO) {
NSLog(@"DID BEGIN SCROLLING");
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(zoomDragDisplayLinkTriggeredForDetails)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
self.scrollIsActive = YES;
}
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
self.scrollIsActive = NO;
[self.displayLink invalidate];
NSLog(@"DID END SCROLLING");
}
Custom method that keeps non-zoomable map elements in the correct x,y position on the display:
Code:
-(void)zoomDragDisplayLinkTriggeredForDetails
{
if (self.viewOfDetailExists) {
CALayer *layerForDetail = self.viewOfDetailShown.layer;
CGPoint currentPoint = layerForDetail.position;
CGPoint convertedPoint = [self.mapView convertPoint:CGPointMake(self.currentPositionOfDetailInScrollView.y, self.currentPositionOfDetailInScrollView.x) toView:self.mapScrollView];
CGPoint secondPoint = [self.mapScrollView convertPoint:convertedPoint toView:self.view];
CGPoint thirdPoint = CGPointMake(secondPoint.x - 35, secondPoint.y - 40);
self.locationDetail = thirdPoint;
CABasicAnimation *basicPositionAnimationX = [CABasicAnimation animationWithKeyPath:@"position.x"];
basicPositionAnimationX.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
basicPositionAnimationX.duration = 1/60.0f;
basicPositionAnimationX.repeatCount = 0;
basicPositionAnimationX.autoreverses = NO;
basicPositionAnimationX.removedOnCompletion = NO;
basicPositionAnimationX.fillMode = kCAFillModeForwards;
basicPositionAnimationX.fromValue = [NSNumber numberWithFloat:currentPoint.x];
basicPositionAnimationX.toValue = [NSNumber numberWithFloat:thirdPoint.x];
CABasicAnimation *basicPositionAnimationY = [CABasicAnimation animationWithKeyPath:@"position.y"];
basicPositionAnimationY.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
basicPositionAnimationY.duration = 1/60.0f;
basicPositionAnimationY.repeatCount = 0;
basicPositionAnimationY.autoreverses = NO;
basicPositionAnimationY.removedOnCompletion = NO;
basicPositionAnimationY.fillMode = kCAFillModeForwards;
basicPositionAnimationY.fromValue = [NSNumber numberWithFloat:currentPoint.y];
basicPositionAnimationY.toValue = [NSNumber numberWithFloat:thirdPoint.y];
[layerForDetail addAnimation:basicPositionAnimationX forKey:@"animatorX"];
[layerForDetail addAnimation:basicPositionAnimationY forKey:@"animatorY"];
}
}
Keep in mind, right now, I only have one actual element on the map that this applies to (custom labels I created) that I call details. Sorry if this method is a bit verbose, I didn't actually intend for anyone else to ever see any of this
