Change Overlay Alpha based on MapType selection - mkmapview

I would like to know if its possible to change the map overlay alpha based on selecting the maptype here is my code I thought might work but it doesn't seem to. Can anyone provide some incite?
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay{
TileOverlayView *view = [[TileOverlayView alloc] initWithOverlay:overlay];
if(mapview.mapType == MKMapTypeHybrid) {
view.tileAlpha = 0.55;
} else if(mapview.mapType == MKMapTypeSatellite) {
view.tileAlpha = 0.0;
} else {
view.tileAlpha = 0.75;
}
return [view autorelease];
}

This code works fine
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
MKCircleView *circleView = [[MKCircleView alloc] initWithCircle:overlay];
CGFloat alpha;
if (mapView.mapType == MKMapTypeStandard) {
alpha = 0.5f;
} else {
alpha = 1.0f;
}
circleView.fillColor = [[UIColor blackColor] colorWithAlphaComponent:alpha];
return circleView;
}
But in mapView:viewForOverlay you set alpha based on current map type. To change alpha when map change map type you need to observe mapType property using KVO. So when map type is changing you just set new alpha for all overlays. To get view for overlay use
[mapView viewForOverlay:(id<MKOverlay>)overlay];

Related

Metal blending alpha under 0.5

When I try to blend with a color with an alpha of 0.5 or below, metal seemingly discards the color like it has an alpha of 0. When I set the alpha to 0.51, I can see it fine. When I set it to 0.5, it's invisible. Here is a simple implementation of the issue:
#implementation Renderer
{
id <MTLDevice> _device;
id <MTLCommandQueue> _commandQueue;
id<MTLLibrary> defaultLibrary;
id <MTLBuffer> _vertexBuffer;
id <MTLBuffer> _indexBuffer;
id <MTLRenderPipelineState> _pipelineState;
}
-(nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)view;
{
self = [super init];
if(self)
{
_device = view.device;
view.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
_commandQueue = [_device newCommandQueue];
[self _createRenderObject];
}
return self;
}
-(void)_createRenderObject
{
Vertex verts[4] = {
simd_make_float2(-0.5f, -0.5f),
simd_make_float2(0.5f,-0.5f),
simd_make_float2(0.5f,0.5f)
};
uint16_t indices[3] = {0,1,2};
_vertexBuffer = [_device newBufferWithBytes:&verts length:sizeof(verts) options:MTLResourceStorageModeShared];
_indexBuffer = [_device newBufferWithBytes:&indices length:sizeof(indices) options:MTLResourceStorageModeShared];
// Create Pipeline State
defaultLibrary = [_device newDefaultLibrary];
MTLRenderPipelineDescriptor *pd = [[MTLRenderPipelineDescriptor alloc] init];
pd.vertexFunction = [defaultLibrary newFunctionWithName: #"VertShader"];
pd.fragmentFunction = [defaultLibrary newFunctionWithName: #"FragShader"];
pd.alphaToCoverageEnabled = YES;
MTLRenderPipelineColorAttachmentDescriptor *cad = pd.colorAttachments[0];
cad.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
cad.blendingEnabled = YES;
cad.alphaBlendOperation = MTLBlendOperationAdd;
cad.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
cad.destinationAlphaBlendFactor = MTLBlendFactorDestinationAlpha;
cad.rgbBlendOperation = MTLBlendOperationAdd;
cad.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
cad.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
NSError *error = NULL;
_pipelineState = [_device newRenderPipelineStateWithDescriptor:pd error:&error];
}
- (void)drawInMTKView:(nonnull MTKView *)view
{
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
id <MTLRenderCommandEncoder> renderEncoder =
[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
[renderEncoder setFrontFacingWinding:MTLWindingCounterClockwise];
[renderEncoder setCullMode:MTLCullModeBack];
[renderEncoder setRenderPipelineState:_pipelineState];
[renderEncoder setVertexBuffer:_vertexBuffer offset:0 atIndex:0];
[renderEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
indexCount:3
indexType:MTLIndexTypeUInt16
indexBuffer:_indexBuffer
indexBufferOffset:0];
[renderEncoder endEncoding];
[commandBuffer presentDrawable:view.currentDrawable];
[commandBuffer commit];
}
#end
Shader.metal:
typedef struct {
float4 position [[position]];
} VertexOut;
vertex VertexOut
VertShader(const uint vertexID [[vertex_id]],
constant Vertex *vertices [[buffer(0)]])
{
VertexOut out;
Vertex v = vertices[vertexID];
out.position = (float4){v.position.x,v.position.y,0,1};
return out;
}
fragment half4
FragShader(VertexOut in [[stage_in]])
{
return half4(1,1,1,0.50f);
}
With that code, specifically the FragShader having 0.50f as the alpha value, I get a blank canvas:
If I change the alpha value to 0.51f:
fragment half4
FragShader(VertexOut in [[stage_in]])
{
return half4(1,1,1,0.51f);
}
I then get this:
Any help is appreciated!
Solved. The problem was that alphaToCoverageEnabled was set to true, while the render target texture type was NOT MTLTextureType2DMultisample. It appears the two work in tandem, but it's beyond my understanding how.
If not using multi-sampling, set alphaToCoverageEnabled to false.
Otherwise, make sure the render target is of type MTLTextureType2DMultisample.
If using MTKView, set the render target texture type by setting the sampleCount on the MTKView object:
_view = (MTKView *)self.view;
_view.sampleCount = 2;
and the render pipeline descriptor of the pipeline state:
MTLRenderPipelineDescriptor *pd = [[MTLRenderPipelineDescriptor alloc] init];
pd.sampleCount = 2;

"Adjust to fit" causes memory leak for UITextField category (or subclass)

When using a category on UITextField, the "Adjust to fit" and minimum font size options may cause a memory leak, when text inside such a field exceeds its visible boundaries. I tried subclassing instead, but it did not solve the problem.
Here is how my category implementation looks like:
#implementation UITextField (custom)
static NSString *fontName = #"My-Awsome-Font";
static UIColor *color;
static UIFont *smallFont;
static UIFont *largeFont;
static NSDictionary *smallAttributes;
static NSDictionary *largeAttributes;
static NSString *placeholder;
static bool shouldModifyPlaceholder;
- (CGRect)textRectForBounds:(CGRect)bounds
{
if(largeFont == nil)
largeFont = [UIFont fontWithName:fontName size:20.0];
if(smallFont == nil)
smallFont = [UIFont fontWithName:fontName size:16.0];
if(smallAttributes == nil)
smallAttributes = #{NSFontAttributeName: smallFont};
if(largeAttributes == nil)
largeAttributes = #{NSFontAttributeName: largeFont};
// general elements
[self setBackgroundColor:[UIColor whiteColor]];
[self.layer setBorderColor:[UIColor whiteColor].CGColor];
[self.layer setBorderWidth:2.0];
self.font = largeFont;
self.layer.cornerRadius = 3;
self.clipsToBounds = YES;
//
shouldModifyPlaceholder = [self respondsToSelector:#selector(setAttributedPlaceholder:)] && ![placeholder isEqualToString:self.placeholder];
// custom elements for each size category
if (self.frame.size.width <= 90) {
if (shouldModifyPlaceholder) {
if ([self.placeholder length] > 3) {
self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:smallAttributes];
}
else {
self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:largeAttributes];
}
placeholder = self.placeholder;
}
return CGRectMake(bounds.origin.x + 18, bounds.origin.y + 9,
bounds.size.width - 36, bounds.size.height - 16);
}
else {
if (shouldModifyPlaceholder) {
self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:largeAttributes];
placeholder = self.placeholder;
}
return CGRectMake(bounds.origin.x + 20, bounds.origin.y + 9,
bounds.size.width - 40, bounds.size.height - 16);
}
}
Recently I discovered that typing "too much" text into a UITextField caused my app to use up all the phone's memory and crash. I found out that it was due to the fact that my customisation did not work well with the "Adjust to fit" option. Removing "Adjust to fit" fixed the issue. I also set minimum font size property to the original font size to be sure. And since I used well over a day trying to find a solution, I thought I'd share this in case someone else runs into this problem.

MKMapView image overlay drawing inverted

I am currently creating an app which requires numerous overlays of different sizes to be drawn on a map. The app currently grabs 2 map points which represent the corners of a bounding box in which the overlay should sit. I am then converting these in to a MKMapRect for use within an MKOverlay class. The overlay is drawing in the correct place but the image seems to be flipped vertically, I can tell this as I tried applying a CGAffineTransformMakeRotation(180) and the image appeared the right way around but the writing was backwards.
If anyone can help me figure out why it's doing this it would be much appreciated, please see my code below.
Setup code:
// First grab the overlay co-ordinates
double left = [[self.storedWildMapObject.arrayBBox objectAtIndex:0] doubleValue], bottom = [[self.storedWildMapObject.arrayBBox objectAtIndex:1] doubleValue], right = [[self.storedWildMapObject.arrayBBox objectAtIndex:2] doubleValue], top = [[self.storedWildMapObject.arrayBBox objectAtIndex:3] doubleValue];
// Store these in 2 coordinates representing top left / bot right
CLLocationCoordinate2D upperLeftCoord =
CLLocationCoordinate2DMake(top,left);
CLLocationCoordinate2D lowerRightCoord =
CLLocationCoordinate2DMake(bottom,right);
//Convert to map points
MKMapPoint upperLeft = MKMapPointForCoordinate(upperLeftCoord);
MKMapPoint lowerRight = MKMapPointForCoordinate(lowerRightCoord);
// Work out sizes
double rightToLeftDiff = lowerRight.y - upperLeft.y;
double topToBottDiff = lowerRight.x - upperLeft.x;
MKMapRect bounds = MKMapRectMake(upperLeft.x, upperLeft.y, topToBottDiff, rightToLeftDiff);
// Add to overlay
MKMapOverlay *mapOverlay = [[MKMapOverlay alloc] initWithMapRect:bounds andCoord:upperLeftCoord];
[self.mapV addOverlay:mapOverlay];
// Map overlay
- (id)initWithMapRect:(MKMapRect)rect andCoord:(CLLocationCoordinate2D)coord
{
self = [super init];
if (self)
{
self.centerPos = coord;
self.mkMapRect = rect;
}
return self;
}
-(CLLocationCoordinate2D)coordinate
{
return self.centerPos;
}
- (MKMapRect)boundingMapRect
{
return self.mkMapRect;
}
// MapOverlayView
- (id)initWithOverlay:(id <MKOverlay>)overlay andImage:(UIImage *)img
{
self = [super initWithOverlay:overlay];
if (self)
{
self.image = img;
}
return self;
}
/** Degrees to Radian **/
#define degreesToRadians( degrees ) ( ( degrees ) / 180.0 * M_PI )
/** Radians to Degrees **/
#define radiansToDegrees( radians ) ( ( radians ) * ( 180.0 / M_PI ) )
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)ctx
{
CGImageRef imageReference = self.image.CGImage;
MKMapRect theMapRect = [self.overlay boundingMapRect];
CGRect theRect = [self rectForMapRect:theMapRect];
CGRect clipRect = [self rectForMapRect:mapRect];
CGContextAddRect(ctx, clipRect);
CGContextClip(ctx);
//CGContextRotateCTM(ctx, degreesToRadians(180.0f));
CGContextDrawImage(ctx, theRect, imageReference);
}
Just incase it helps I also have a debugger print out of theMapRect as used in MapOverlayView:
(lldb) p theMapRect
(MKMapRect) $17 = {
(MKMapPoint) origin = {
(double) x = 1.27662e+08
(double) y = 7.86099e+07
}
(MKMapSize) size = {
(double) width = 8.12378e+06
(double) height = 1.27474e+07
}
}
Below is what it looks like:
Have you verified that the storedwildmapobject has the coordinates in the order you are expecting them?
The answer found here seems to draw my image the correct way around with the same mapRect:
https://stackoverflow.com/a/3599448/1090024
So I'm using this for now.

How do you programmatically position the mkmapregion to fit annotation

I'm trying to figure out how to reposition the MKMap region programmatically so that my annotation (automatically selected when the map loads) will all fit centered.
Current Result: https://www.evernote.com/shard/s46/sh/7c7d2ed8-203c-4878-af8c-83ff77ad7b21/ce7786acdf66b0782fc689b72d1b67e7
Desired Result: https://www.evernote.com/shard/s46/sh/21fb0eab-d5c4-4e6d-b05b-322e7dcce8ab/ab816f2a24f11b9c9e15bf55ac648f72
I have tried to reposition everything in - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views but that didn't work. Is there a better approach?
// here is viewWillAppear logic
[self.mapView removeAnnotations:self.mapView.annotations];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLGeocodeCompletionHandler completionHandler = ^(NSArray *placemarks, NSError *error) {
if (error) {
EPBLog(#"error finding placemarks: %#", [error localizedDescription]);
} else {
if (placemarks) {
[placemarks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CLPlacemark *placemark = (CLPlacemark *)obj;
if ([placemark.country isEqualToString:#"United States"]) {
EPBAnnotation *annotation = [EPBAnnotation annotationWithCoordinate:placemark.location.coordinate];
annotation.title = self.locationObj.locationName;
annotation.subtitle = self.locationObj.locationAddress;
[self.mapView addAnnotation:annotation];
self.mapView.selectedAnnotations = #[annotation];
[self.mapView setCenterCoordinate:placemark.location.coordinate];
/**
* #todo
* MOVE THIS OUTTA HERE
*/
MKCoordinateRegion region = {{0.0f, 0.0f}, {0.0f, 0.0f}};
region.center = placemark.location.coordinate;
region.span.longitudeDelta = 0.003f;
region.span.latitudeDelta = 0.003f;
[self.mapView setRegion:region animated:YES];
[self.mapView regionThatFits:region];
*stop = YES;
}
}];
}
}
};
[geocoder geocodeAddressString:self.locationObj.locationAddress completionHandler:completionHandler];
Following method will fit the map on region to show all annotations. You can call this method in Map's didAddAnnotations method.
- (void)zoomToFitMapAnnotations {
if ([mMapView.annotations count] == 0) return;
int i = 0;
MKMapPoint points[[mMapView.annotations count]];
//build array of annotation points
for (id<MKAnnotation> annotation in [mMapView annotations]){
points[i++] = MKMapPointForCoordinate(annotation.coordinate);
}
MKPolygon *poly = [MKPolygon polygonWithPoints:points count:i];
[mMapView setRegion:MKCoordinateRegionForMapRect([poly boundingMapRect]) animated:YES];
}
Howevcer you should see if you want to add user location annotation in visible area also. If you don't then in loop check if current annotation is MkUserLocation and don't add it's points in points array.
if ([annotation isKindOfClass:[MKUserLocation class]]) {
continue:
}
Now if you wanted an Annotation to be in center and selected automatically then do this
annotation.coordinate=mMapView.centerCoordinate;
[mMapView selectAnnotation:annotation animated:YES];

Mapkit Annotation type when zooming in and out?

i am working with Mapkit and i am on SDK 4.2, i am having a strange bug here, in fact i have 3 annotation types, "blue.png", red.png,black.png. I am loading these by a flux and depending on the type its will select these annotation types. Everything works fine when the map is loaded i have the the different annotation view, but when i move , zoom in or zoom out the annotation view changes i.e where it was supposed to be blue.png it becomes black.png.
I am actually testing it on device.
Thank you very much :)
Hey veer the problem is that this method is called if the user pans the map to view another location and then comes back to the place where the annotations are plotted.
- (MKAnnotationView *)mapView:(MKMapView *)mapview viewForAnnotation:(id <MKAnnotation>)annotation
I have seen many sample code for map application and this in what most of the people are using.
- (MKAnnotationView *)mapView:(MKMapView *)mapview viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if(annotationView)
return annotationView;
else
{
MKPinAnnotationView* pinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
pinView.animatesDrop=YES;
pinView.canShowCallout=YES;
pinView.draggable = YES;
pinView.pinColor = MKPinAnnotationColorGreen;
return pinView;
}
return nil;
}
i found the solution - in fact i am using a custom annotation view and having 3 diff types of images :
Soln:
- (AnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
AnnotationView *annotationView = nil;
// determine the type of annotation, and produce the correct type of annotation view for it.
AnnotationDetails* myAnnotation = (AnnotationDetails *)annotation;
if(myAnnotation.annotationType == AnnotationTypeGeo)
{
// annotation for your current position
NSString* identifier = #"geo";
AnnotationView *newAnnotationView = (AnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(nil == newAnnotationView)
{
newAnnotationView = [[[AnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:identifier] autorelease];
}
annotationView = newAnnotationView;
}
else if(myAnnotation.annotationType == AnnotationTypeMyfriends)
{
NSString* identifier = #"friends";
AnnotationView *newAnnotationView = (AnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(nil == newAnnotationView)
{
newAnnotationView = [[[AnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:identifier] autorelease];
}
annotationView = newAnnotationView;
}
}

Resources