Black box over MKMapView - objective-c

I have an MKMapView and another UIView subclass that is overlaid on top of the map. The custom view is transparent (I use it to overlay an inner shadow around the edge of the map). Now when I drop a pin on the map view a small black square shows up over the pin. It seems to disappear and change size randomly too. I can't take a screenshot of it because for some reason it doesn't appear in screenshots. I know it's related to the custom view that's overlaid on the map because when I remove it everything works fine.
Any ideas?
EDIT: As requested here's the screenshot
and the code for the shadow overlay view:
self.layer.cornerRadius = 8.0f;
self.layer.borderColor = [UIColor colorWithWhite:0.8f alpha:0.6f].CGColor;
self.layer.borderWidth = 1.0f;
self.layer.masksToBounds = YES;
self.clipsToBounds = YES;
self.layer.shadowOffset = CGSizeMake(0, 1);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 1.0;
I place the shadow overlay view in IB and that code is in the drawRect method of the view subclass.

Put your non-clipped shadow view inside another view with clipping on, this will clip the shadow on the outside of the box.

Related

outside border for UIImageView

I know I can create border using below code.
[[myImageView layer] setBorderWidth:2.0f];
[[myImageView layer] setBorderColor:[UIColor greenColor].CGColor];
However this draw border inside image.
What I was looking is draw border outside ImageView.
Note:
I search online for this and found below.
Can be done by using another image which will have border.
Can be done by drawing another view which is little bigger then current image.
Is there quick way (especially in-built in iOS), where I can draw border outside UIImageView? Any views?
Why don't you try border with using imageview's frame ?
CGFloat borderWidth = 5.0f;
self.imgview.frame = CGRectInset(self.imgview. frame, -borderWidth, -borderWidth);
self.imgview. layer.borderColor = [UIColor blueColor].CGColor;
self.imgview. layer.borderWidth = borderWidth;
There is no quickway in-built in iOS, there is no margin that you could set on the image layer.
If I were you, I'd develop a new class that inherit from UIView (ex UIImageWithBorderView) and which include a UIImageView and making the "UIImageWithBorderView" bigger than the UIImageView (and think about NOT to autoresize the UIImageView with the UIView parent, otherwise your UIImageView will be stretched, and prevent the UIImageWithBorderView from being smaller than the UIImageView frame), and then add borders to the "UIImageWithBorderView".
This way, your UIImageView will be intact and you'll have a specific, reusable composant for your needs.
Hope it helps !

NSScrollView: fade in a top-border like Messages.app

What I Want to Do:
In Messages.app on OS 10.10, when you scroll the left-most pane (the list of conversations) upwards, a nice horizontal line fades in over about 0.5 seconds. When you scroll back down, the line fades back out.
What I Have:
I am trying to achieve this effect in my own app and I've gotten very close. I subclassed NSScrollView and have done the following:
- (void) awakeFromNib
{
_topBorderLayer = [[CALayer alloc] init];
CGColorRef bgColor = CGColorCreateGenericGray(0.8, 1.0f);
_topBorderLayer.backgroundColor = bgColor;
CGColorRelease(bgColor);
_topBorderLayer.frame = CGRectMake(0.0f, 0.0f, self.bounds.size.width, 1.0f);
_topBorderLayer.autoresizingMask = kCALayerWidthSizable;
_topBorderLayer.zPosition = 1000000000;
_fadeInAnimation = [[CABasicAnimation animationWithKeyPath:#"opacity"] retain];
_fadeInAnimation.duration = 0.6f;
_fadeInAnimation.fromValue = #0;
_fadeInAnimation.toValue = #1;
_fadeInAnimation.removedOnCompletion = YES;
_fadeInAnimation.fillMode = kCAFillModeBoth;
[self.layer insertSublayer:_topBorderLayer atIndex:0];
}
- (void) layoutSublayersOfLayer:(CALayer *)layer
{
NSPoint origin = [self.contentView documentVisibleRect].origin;
// 10 is a fudge factor for blank space above first row's actual content
if (origin.y > 10)
{
if (!_topBorderIsShowing)
{
_topBorderIsShowing = YES;
[_topBorderLayer addAnimation:_fadeInAnimation forKey:nil];
_topBorderLayer.opacity = 1.0f;
}
}
else
{
if (!_topBorderIsShowing)
{
_topBorderIsShowing = NO;
// Fade out animation here; omitted for brevity
}
}
}
The Problem
The "border" sublayer that I add is not drawing over top of all other content in the ScrollView, so that we end up with this:
The frames around the image, textfield and checkbox in this row of my outlineView are "overdrawing" my border layer.
What Causes This
I THINK this is because the scrollView is contained inside an NSVisualEffectView that has Vibrancy enabled. The reason I think this is that if I change the color of my "border" sublayer to 100% black, this issue disappears. Likewise, if I turn on "Reduce Transparency" in OS X's System Preferences > Accessibility, the issue disappears.
I think the Vibrancy compositing is taking my grey border sublayer and the layers that represent each of those components in the outlineView row and mucking up the colors.
So... how do I stop that for a single layer? I've tried all sorts of things to overcome this. I feel like I'm 99% of the way to a solid implementation, but can't fix this last issue. Can anyone help?
NB:
I am aware that it's dangerous to muck directly with layers in a layer-backed environment. Apple's docs make it clear that we can't change certain properties of a view's layer if we're using layer-backing. However: adding and removing sublayers (as I am) is not a prohibited action.
Update:
This answer, while it works, causes problems if you're using AutoLayout. You'll start to get warnings that the scrollView still needs update after calling Layout because something dirtied the layout in the middle of updating. I have not been able to find a workaround for that, yet.
Original solution:
Easiest way to fix the problem is just to inset the contentView by the height of the border sublayer with this:
- (void) tile
{
id contentView = [self contentView];
[super tile];
[contentView setFrame:NSInsetRect([contentView frame], 0.0, 1.0)];
}
Should have thought of it hours ago. Works great. I'll leave the question for anyone who might be looking to implement these nice fading-borders.

Pinch Gesture Zoom only Zooms UIImage from Upper Left Corner

I am fairly new to this and I'm trying to use pinch gestures to zoom in on a UIImage and be able to zoom into any specific part of the image. However, when I zoom it only zooms from the upper left corner of the UIView. So I only get to see the upper left corner of the image zoomed in. I'd like to be able to zoom/pan the image similar to how the Photos app works. Here is my code so far:
In ViewDidLoad:
...
// Load the image to be viewed into the UIImage
self.theImage.image = self.theNewImage;
UIPinchGestureRecognizer *pinchGestRecog = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(twoFingerPinch:)];
// ADD GESTURE RECOGNIZER TO THE VIEW
[theImage addGestureRecognizer:pinchGestRecog];
// ALLOW USER INTERACTION ON THE VIEW
[theImage setUserInteractionEnabled:YES];
// SET IMAGE ZOOM SCALE LIMITS
imageCurrentScale = 1.0;
imageMaxScale = 2.0;
imageMinScale = 0.5;
And then in my twoFingerPinch method:
- (void)twoFingerPinch:(UIPinchGestureRecognizer *)aPinchGesture
{
if (imageCurrentScale * [aPinchGesture scale] > imageMinScale && imageCurrentScale * [aPinchGesture scale] < imageMaxScale) {
imageCurrentScale = imageCurrentScale * [aPinchGesture scale];
CGAffineTransform zoomTransform = CGAffineTransformMakeScale(imageCurrentScale, imageCurrentScale);
[[aPinchGesture view] setTransform:zoomTransform];
}
[aPinchGesture setScale:1.0];
}
Is panning somehow the answer? I'm not really sure how panning works.
Any suggestions? Thanks.
The reason why you zoom to your upper left corner is because the center of your scale transformation is the UIView.layer.anchorPoint which is default the upper left corner (0,0).
If you want to zoom correctly you somehow have to figure out the center of your pinch gesture and then set your anchorPoint to that position.
But you shouldn't do that anyway. The recommended way to zoom into a UIImageView is to place the the image view into a UIScrollView and set the contentSize and maximumZoomScale and minimumZoomScale values accordingly.
Set yourself as the delegate of your UIScrollView and return the UIImageView in the viewForZooming delegate method.
The scrollview will handle the pinching and zooming for you.

CALayer cornerRadius has no effect on 4" device (simulator)

I work on dynamically, programmatically layout a view for 3.5" devices as well as for 4" devices.
As such that works fine.
But I want rounded corners so that my images appear like playing cards.
And I get rounded corners nicely displayed in 3,5 inch devices on the simulator for simulated iOS 6.1 and 7 alike.
But when I choose iPhone retina 4 inch on 6.1 or 7, then the UIImage in the UIImageView is fully displayed.
It works nicely on simulated iPad devices (in iPhone simulation mode - it is an iPhone only app).
As for today, I do not have any 4" device with me to test it. I can test on a device during the upcoming week.
Hiere is the relevant code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.imageV.image = self.image; // The image property was set by the caller.
// Layout imageV within self.view with a margin of MARGIN
self.imageV.frame = CGRectMake(self.view.frame.origin.x + MARGIN, self.view.frame.origin.y + MARGIN, self.view.frame.size.width - 2 * MARGIN, self.view.frame.size.height - 2 * MARGIN);
// set the raidus and the mask to follow the rounded corners.
self.imageV.layer.cornerRadius = CORNER_RADIUS;
self.imageV.layer.masksToBounds = YES;
}
BTW: CORNER_RADIUS is 18 and MARGIN is 15. Changing these values has no effect on the issue.
UPDATE: Thanks to Matt I figured out that the problem disappears when I create the UIImageView programmatically. That is some really nice workaround plus it points into the right diretion, I guess, but it is not a solution. Any ideas what setting in the storyboard editor might have caused the problem?
As far as I can see, auto layout is disabled for all view controllers in this storyboard.
The answer is simple. The code did work. It did add round corners to the UIImageView object and the maskToBounds worked well.
But the actual image displayed is smaller. I used AspectFit as mode to ensure that the actual image is not squeesed but displayed in its original aspect ration. Because of the longer layout of the iPhone5 dimensions the image only filled a part of its owning UIImageView. I changed the background color to gray for the screenshot and now it gets clear.
So the solution will be that I'll have to calculate the proper size of the image view so that it matches exactly the size of the scaled image. Then it should work.
(I'll update this answer when it is done).
Update: this is what I finally did: I removed the UIImageView from the Storyboard and deal with it programmatically.
Don't get confused by the complexity. I added another view just to throw a shadow, although this is not related to the original question. The shadow I wanted to add anyway. And it turned out that CALayer's shadow and masksToBounds=YES don't really agree on. That is why I added a regular UIView which lies in between the card view and the background view.
Finally this is so much of a hassle for displaying a simple rectangle image, that I think, just subclassing UIView and drawing everything with openGL or so directly into the CALayer would be probably much easier. :-)
Anyway, this is my code:
- (void)viewDidLoad {
[super viewDidLoad];
self.state = #0;
// Create an image view to carry the image with round rects
// and create a regular view to create the shadow.
// Add the shadow view first so that it appears behind
// the actual image view.
// Explanation: We need a separate view for the shadow with the same
// dimenstions as the imageView. This is because the imageView's image
// is rectangular and will only be clipped to round rects when the
// property masksToBounds is set to YES. But this setting will also
// clip away any shadow that the imageView's layer may have.
// Therfore we add a separate mainly empty UIView just behind the
// UIImageview to throw the shadow.
self.shadowV = [[UIView alloc] init];
self.imageV = [[UIImageView alloc] initWithImage:self.image];
[self.view addSubview:self.shadowV];
[self.shadowV addSubview:self.imageV];
// set the raidus and the mask to follow the rounded corners.
[self.imageV.layer setCornerRadius:CORNER_RADIUS];
[self.imageV.layer setMasksToBounds:YES];
[self.imageV setContentMode:UIViewContentModeScaleAspectFit];
// set the shadows properties
[self.shadowV.layer setShadowColor:[UIColor blackColor].CGColor];
[self.shadowV.layer setShadowOpacity:0.4];
[self.shadowV.layer setShadowRadius:3.0];
[self.shadowV.layer setShadowOffset:CGSizeMake(SHADOW_OFFSET, SHADOW_OFFSET)];
[self.shadowV.layer setCornerRadius:CORNER_RADIUS];
[self.shadowV setBackgroundColor:[UIColor whiteColor]]; // The view needs to have some content. Otherwise it is not displayed at all, not even its shadow.
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Just to be save
if (!self.image) {
return;
}
self.imageV.image = self.image; // The image property was set by the caller.
// Layout imageV within self.view with a margin of MARGIN
self.imageV.frame = CGRectMake(MARGIN, MARGIN, self.view.bounds.size.width - 2 * MARGIN, self.view.bounds.size.height - 2 * MARGIN);
// Calculate the size and position of the image and set the image view to
// the same dimensions
// This works under the assumption, that the image content mode is aspectFit.
// Well, as we are doing so much of the layout manually, it would work with a number of content modes. :-)
float imageWidth, imageHeight;
float heightWidthRatioImageView = self.view.frame.size.height / self.view.frame.size.width;
float heightWidthRatioImage = self.image.size.height / self.image.size.width;
if (heightWidthRatioImageView > heightWidthRatioImage) {
// The ImageView is "higher" than the image itself.
// --> The image width is set to the imageView width and its height is scaled accordingly.
imageWidth = self.imageV.frame.size.width;
imageHeight = imageWidth * heightWidthRatioImage;
} else {
// The ImageView is "wider" than the image itself.
// --> The image height is set to the imageView height and its width is scaled accordingly.
imageHeight = self.imageV.frame.size.height;
imageWidth = imageHeight / heightWidthRatioImage;
}
// Layout imageView and ShadowView accordingly.
CGRect imageRect =CGRectMake((self.view.bounds.size.width - imageWidth) / 2,
(self.view.bounds.size.height - imageHeight) / 2,
imageWidth, imageHeight);
[self.shadowV setFrame:imageRect];
[self.imageV setFrame:CGRectMake(0.0f, 0.0f, imageWidth, imageHeight)]; // Origin is (0,0) because it overlaps its superview which just throws the shadow.
}
And this is how it finally looks like:
The problem is due to some issue with code or configuration you have not told us about. Proof: I ran the following and it works fine. Note that I create the image view in code (to avoid the auto layout problem) and fixed your frame/bounds confusion, and that I've skipped your self.image, but none of that is really relevant to the issue you are seeing:
#define CORNER_RADIUS 18
#define MARGIN 15
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.imageV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"im"]];
[self.view addSubview:self.imageV];
// Layout imageV within self.view with a margin of MARGIN
self.imageV.frame = CGRectMake(self.view.bounds.origin.x + MARGIN, self.view.bounds.origin.y + MARGIN, self.view.bounds.size.width - 2 * MARGIN, self.view.bounds.size.height - 2 * MARGIN);
// set the raidus and the mask to follow the rounded corners.
self.imageV.layer.cornerRadius = CORNER_RADIUS;
self.imageV.layer.masksToBounds = YES;
}
It works fine (and you can prove that to yourself). Here is a screen shot of the 4-inch simulator:
Therefore the problem is outside the code that you quote in your question, and cannot be analyzed without further information.

Resize MKMapView

Looks like simple task. But when I try to resize using setFrame method I got glitches. There are some other UIViews resized using setFrame method and it works perfectly. I made custom application with slider bar and map view. SlideBar changes X position for MKMapView, but keeps width equals to screen width. This approach works fine for all Views. But MKMapView resizes with smooth troubles. Can anyone please give a clue why it's happening and how to solve it?
I had the same problem, which seems to occur only on iOS 6 (Apple Maps) and not on iOS 5 (Google Maps).
My solution was to take a "screenshot" of the map when the user start dragging the divider handle, replace the map with this screenshot during the drag, and put the map back when the finger is released.
I used the code from How to capture UIView to UIImage without loss of quality on retina display for UIView screenshot and Nikolai Ruhe's answer at How do I release a CGImageRef in iOS for a nice background color.
My UIPanGestureRecognizer action is something like this (the (MKMapView)self.map is a subview of (UIView)self.mapContainer with autoresizing set on Interface Builder):
- (IBAction)handleMapPullup:(UIPanGestureRecognizer *)sender
{
CGPoint translation = [sender translationInView:self.mapContainer];
// save current map center
static CLLocationCoordinate2D centerCoordinate;
switch (sender.state) {
case UIGestureRecognizerStateBegan: {
// Save map center coordinate
centerCoordinate = self.map.centerCoordinate;
// Take a "screenshot" of the map and set the size adjustments
UIImage *mapScreenshot = [UIImage imageWithView:self.map];
self.mapImage = [[UIImageView alloc] initWithImage:mapScreenshot];
self.mapImage.autoresizingMask = self.map.autoresizingMask;
self.mapImage.contentMode = UIViewContentModeCenter;
self.mapImage.clipsToBounds = YES;
self.mapImage.backgroundColor = [mapScreenshot mergedColor];
// Replace the map with a screenshot
[self.map removeFromSuperview];
[self.mapContainer insertSubview:self.mapImage atIndex:0];
} break;
case UIGestureRecognizerStateChanged:
break;
default:
// Resize the map to the new dimension
self.map.frame = self.mapImage.frame;
// Replace the screenshot with the resized map
[self.mapImage removeFromSuperview];
[self.mapContainer insertSubview:self.map atIndex:0];
// Empty screenshot memory
self.mapImage = nil;
break;
}
// resize map container according do the translation value
CGRect mapFrame = self.mapContainer.frame;
mapFrame.size.height += translation.y;
// reset translation to make a relative read on next event
[sender setTranslation:CGPointZero inView:self.mapContainer];
self.mapContainer.frame = mapFrame;
self.map.centerCoordinate = centerCoordinate; // keep center
}