Update AnnotationView after/during zoom - iphone

I need to change the centerOffset property of an AnnotationView when the zoomlevel changed. I don't want to remove and add the Annotations again.
So how can I access and update the AnnotationViews after zooming?
Any ideas on this?
Best Regards,
Christian

One solution would be to use these MKMapView methods :
- (NSSet *)annotationsInMapRect:(MKMapRect)mapRect // iOS4.2+ only!
to retrieve visible Annotations, then use :
- (MKAnnotationView *)viewForAnnotation:(id<MKAnnotation>)annotation;
To get the corresponding view (should never return nil if first function does it job correctly)
Then I guess changing centerOffset should do the job, (plus maybe some -[UIView setNeedsDisplay/Layout] to force a redraw)
Changing centerOffset depending on zoomLevel sounds strange, but I guess you have your reasons :)

Related

How can I know if an annotation is already on the mapview

I trying to add annotation pin to Mapview if and only if there is no such same pin already on mapview to avoid having multiple repeat annotations at the same location.
Any one can show me help?
You can use mapView.view(for:) method.
e.g.
if (self.mapView.view(for: annotation) != nil) {
print("pin already on mapview")
}
The approach suggested by #Kosuke Ogawa would work. However it is not a good idea to rely on the UI determine application state.
For eg: You most probably have a list of annotations stored in some sort of data structure in your view controller (I am guessing an Array). This data structure should be the source of truth.
In your case, to determine if a an Annotation is already on the map, check if the Data structure that feeds the mapview contains the annotation and proceed.

iOS: UIView's origin with respect to UIWindow

A UIView has an origin contained in its frame or bounds properties with respect to its superview. Say I want to do something which grabs the origin of a UIView with respect to the UIWindow instead. Do I have to go each step up the hierarchy in order to make this calculation or is there a simpler, more direct way?
I guess you looking for this method
convertPoint:toView
to find out where a view aView is in the application window, try
[aView convertRect:aView.frame toView:[UIWindow screen]]
This method is in the UIView Class Reference
The best and easiest way to do this is by using convertPoint:toView or convertRect:toView on UIWindow. In order to do this correctly, you need to supply a view whose window matches the window of the view whose coordinates you are trying to convert.
For your question, you need to know a view's origin with respect to its location in the window. Thankfully, you don't need to know the window in order to get this. According to the documentation of UIView's convert:to methods.
The view into whose coordinate system point is to be converted. If view is nil, this method instead converts to window base coordinates. Otherwise, both view and the receiver must belong to the same UIWindow object.
This means that you can just supply nil to achieve what you need!
In Swift:
let pointInWindow = yourView.convert(.zero, to: nil)
In Objective-C:
CGPoint pointInWindow = [yourView convertPoint:CGPointZero toView:nil];

Rotation - slowing it down using willAnimateSecondHalfOfRotationFromInterfaceOrientation

people, how do you actually slow down the orientation-rotation when using the "willAnimateSecondHalfOfRotationFromInterfaceOrientation:" method?
I currently have this:
-(void) willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
duration:(NSTimeInterval)duration {
[self positionViews];
}
And I understand that this "willAnimate2ndHalf..." method gets called automatically when the rotation does indeed happen - well where do I actually get to change its DURATION value?
If you want to change the overall timing of he app's rotation, it can't be done. willAnimateSecondHalfOfRotationFromInterfaceOrientation is meant for adding custom code, like setting custom coordinates, properties, things like that.

Do I have to implement mapView:regionWillChangeAnimated:on my MKMapview delegate?

Apple's docs tell you this method should be as lightweight as possible, what's a standard use here? Resetting the annotation pins?
Tells the delegate that the region
displayed by the map view is about to
change.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
Parameters
mapView
The map view whose visible region is
about to change.
animated
If YES, the change to the new region
will be animated. If NO, the change
will be made immediately.
This method is called whenever the
currently displayed map region
changes. During scrolling, this method
may be called many times to report
updates to the map position.
Therefore, your implementation of this
method should be as lightweight as
possible to avoid affecting scrolling
performance.
The problem with this delegate method is "During scrolling, this method may be called many times to report updates to the map position" (so you need IF/THEN or CASE/BREAK, etc to keep it "lightweight").
You don't NEED to use this method at all (not required), but if you do wish to incorporate some sort of functionality (such as removing worthless pins, etc), then example code to keep it lightweight would be:
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated{
if(!animated){
//Instantaneous change, which means you probably did something code-wise, so you should have handled anything there, but you can do it here as well.
} else {
//User is most likely scrolling, so the best way to do things here is check if the new region is significantly (by whatever standard) away from the starting region
CLLocationDistance *distance = [mapView.centerCoordinate distanceFromLocation:originalCoordinate];
if(distance > 1000){
//The map region was shifted by 1000 meters
//Remove annotations outsides the view, or whatever
//Most likely, instead of checking for a distance change, you might want to check for a change relative to the view size
}
}
}

iPhone scrollView add elements dynamically with id

I want to populate a scrollView with quite a few different UI elements.
Therefore I thought I would write a method that remembers the current Position in the scrollView and just adds the element to the scrollView at the current Position.
Something like:
- (void)addUIElement:(id)element withWidth:(CGFloat)width andHeight:(CGFloat)height andYGap:(CGFloat)YGap {
element.frame = CGRectMake(currentScrollPos.x, (currentScrollPos.y + YGap), width, height);
[scrolly addSubview:element];
//And then set the current scroll position here
}
Unfortunately when I try to do access element.frame = ..., I get request for member in something not a structure or union. When I try to do [element frame] = ... Lvalue required as left operand of assignment.
Now, first of all I am not sure what's the best way to dynamically add objects to a scrollview. Maybe anyone has a better or easier approach.
Then on the other hand, I don't get why the above does not work?! Would I have to cast my element to the actual class? I thought I would not have to do so... Also then my method would not make that much sense anymore. Or at least would require some more steps...
This should work I think:
[element setFrame:...];
However if you work with different UI elements in your method may be you can make your elements parameter UIView* instead of id? This way your code will work for all UIView subclasses (which is what you actually need I suppose)
The difference is that "id" doesn't have any kind of reference to a frame. It could be anything. You want to instead do (UIView *)element in the method declaration, or alternatively in the call to element.frame, you would do ((UIView *)element).frame.
(And yeah, all things that you put on the screen are inheriting from UIView -- UIButton, UIImageView, etc.)