CATiledLayer Drop Shadow Rendering Incorrect - iphone

I have a view that's rendering a PDF into a CATiledLayer. This is working well.
Now I'm attempting to add a drop shadow to the view, so I did the usual:
tiledLayer.masksToBounds = NO;
tiledLayer.shadowOffset = CGSizeMake(5, 5);
tiledLayer.shadowRadius = 5;
tiledLayer.shadowOpacity = 0.5;
tiledLayer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
But what I'm seeing rendered is incorrect. What seems to be happening is that there's a drop shadow drawn for each tile as the tiles are being drawn. Once all the tiles are drawn, the final product looks right and has a shadow in the right place, but the intermediate rendering is distracting.
How can I use a drop shadow with a CATiledLayer?

You should wrap your CATiledLayer in a container layer with those shadow attributes applied to it. Judging by the self.bounds call, you might already be embedding the CATiledLayer in a view’s layer, in which case (unless you need masksToBounds) you can just apply the shadow attributes to that layer directly.

It makes perfect sense that a drop shadow is being drawn for each tile, that is exactly what you told it to do. I assume you have your tiles loading in some kind of parent view that contains them (usually a UIScrollView). You should set your drop shadow on that containing view.

Related

Map Center & Bounds do not update when Container Div is Resized

(pretty new to Mapbox and JS, so I'm in a bit over my head)
I'm working on a page where the user needs to adjust the size of map container. It needs to be able to get accurate bounds and center point of the resized map (via map.getBounds and map.getCenter).
When I use JS to adjust the size of the container div, the center and bounds are not adjusted.
Fiddle: http://jsfiddle.net/xcow63sm/3/
Panning/zooming results in updated center and bounds. Browser window resize (if you have width or height set to 100%) works too. I would expect changing the container dimensions would adjust center and bounds.
However, if you use the form fields to adjust the height/width of the container div, the center and bounds do not. I've tried (with increasing desperation):
function resize (){
var inputwidth = document.getElementsByName("mapwidth")[0].value;
var inputheight = document.getElementsByName("mapheight")[0].value;
var mapDiv = document.getElementById('map');
var mapcenter = map.getCenter();
mapDiv.style.width = inputwidth;
mapDiv.style.height = inputheight;
map.updateSize();
map.update();
map.resize();
map.invalidateSize();
document.getElementById("mapcenter").value = mapcenter;
Edit: this seems to apply but I can't make sense of it: Resizing a leaflet map on container resize
If I understand you, you have two issues: you don't like how the map is positioned after its size changes, and you're getting white space.
Map positioning after resize
There are at least three valid options for how a map should update if, say, its width and height are suddenly doubled:
keep the same northern and western bound, extend the eastern and southern bounds. (Expanding the viewing area right and down)
move all four bounds outwards (keeping the centre of the map in the same place)
leave all four bounds where they are are, but change the zoom, so the same geographic area is displayed, but in more detail.
I think your issue is that Mapbox is choosing option 1, but you want one of the other two options. The solution, as I think you've discovered, is simply to do your own maths and set the bounds how you want them.
Map failing to repaint correctly
Your second issue is that when you resize, calling map.resize() isn't updating the internal size of the map properly, and you're left with white space. The reason for this is you're using animated CSS properties. So when you do this:
mapDiv.style.width = inputwidth;
mapDiv.style.height = inputheight;
map.resize();
This doesn't work because the map's size hasn't actually transitioned to the new size yet. You can work around it like this:
mapDiv.style.width = inputwidth;
mapDiv.style.height = inputheight;
window.setTimeout(()=>map.resize(), 500);
There's probably a better event you can listen to, to keep updating as the map area expands.
Updated fiddle here: http://jsfiddle.net/xcow63sm/11/
Btw you should consult the documentation here. I'm not sure where you got map.updateSize() from but that's not Mapbox-GL-JS :)

Getting the outer CGRect

I have two UIViews, both of which obviously have CGRects each. One UIView fills the screen whereas the other just fills part of it in the middle. I have the CGRect of the smaller UIView, but how can I get the CGRect of the one outside of it - excluding the size of the smaller one so that the background can be dimmed, but not the content of the inner UIView?
This is what my UIViewController looks like so you can get a better idea of what I'm trying to do:
I want to dim the outer UIView, not the inner one - but I don't have the CGRect of the outer one excluding the inner UIView so I've had to do it the other way around for now.
A CGRect is a square size. If you want to know the outer aria you need to compute 4 CGRects. The top, left, right and bottom space.
So, it looks to me like the outer view is responsible for drawing your content and the inner view is your selection rectangle right?
If so, then I think you are going to want to draw your content twice when you have this selection mode active. You will:
draw one pass dimmed - adjust the colors yourself (you can either draw the whole area or clip away the inner area using code from here)
draw the second inner pass with a clip rect setup so only your content inside the rect for your selection rectangle is drawn
Or
draw all your content like normal
set your clipping mask (using code from here so that just the outer area is rendered) and then draw a black rect with some degree of transparency over your whole scene

UIView: opaque vs. alpha vs. opacity

How do opaque alpha and the opacity of the background work together for a UIView and what are the differences between them?
UIView http://i.minus.com/jb2IP8TXbYTxKr.png
opaque means don't draw anything underneath, even if you are transparent.
The background color's alpha only affects the background color's transparency, not anything else drawn on the view.
alpha affects everything drawn on the view.
The opaque property can give you a speed increase - if you know that your view will never have transparency you can set this to YES and when iOS renders your view it can make some performance optimisations and render it faster. If this is set to NO iOS will have to blend your view with the view underneath, even if it doesn't happen to contain any transparency.
The alpha will also affect the alpha of the backround color i.e. if the background color is 0.5 transparent and the alpha is also 0.5, this has the effect of making the background view's alpha 0.25 (0.5 * 0.5).
To the very good answer by deanWombourne it's worth to add that, unless you don't draw your own content using the drawRect: method, the opaque property has no effect.
Apple's doc:
You only need to set a value for the opaque property in subclasses of
UIView that draw their own content using the drawRect: method. The
opaque property has no effect in system-provided classes such as
UIButton, UILabel, UITableViewCell, and so on.
If you draw your own content, keep in mind, that opaque is just a hint
This property provides a hint to the drawing system as to how it
should treat the view.
and some more guidelines from the same Apple's doc:
If the view is opaque and either does not fill its bounds or contains
wholly or partially transparent content, the results are
unpredictable. You should always set the value of this property to NO
if the view is fully or partially transparent.
iOS alpha vs opacity vs opaque
[Color Blended Layers]
UIView.alpha is equal to CALayer.opacity - [0.0 - 1.0] - apply alpha to all view(subviews, sublayers). It's like make a single flat bitmap image based on all content and apply alpha. That is why if view contains another subview or sublayer - no Blended Layers and is Off-screen Rendered is applied
UIView.backgroundColor is equal to CALayer.backgroundColor - applies color only on background(not subviews, sublayers)
UIView.opaque is equal to CALayer.opaque - Boolean property which hint's framework about some optimizations. But on practice it is not visible
Experiments
input
alpha
view1.alpha = 0.5
//or
view1.layer.opacity = 0.5
result and Off-screen Rendered
backgroundColor
view1.backgroundColor = .cyan.withAlphaComponent(0.5)
//or
view1.layer.backgroundColor = UIColor.cyan.withAlphaComponent(0.5).cgColor
result and Blended Layers

Creating a stroke/outline of a .PNG with CALayer?

I would like to apply a "stroke" or outline to a png, identically to how Photoshop does it. I have a feeling this can be done with CALayer, but after some tinkering, it is not immediately obvious. setBorderWidth + setBorderColor is almost what I want, except that it only adds a border to the entire dimension of the image, rather than the outline of the png image itself.
Once the stroke is applied, I'd like to also knockout the fill of the png, leaving only an outlined border of the initial shape.
There is no automatic way to do what you're asking. You have to know the path of the shape within your png that you want to "knockout". Once you've defined that, you can create a CAShapeLayer, which accepts a CGPathRef, containing your points. You can stroke and fill the path layer with whatever color you choose and then add it to the layer hierarchy of the displaying view or use it to define a mask of one of the layers in your view.

Increase UIView's frame size at the left edge

I have an UIView that can grow dynamically in width. The view also have subviews located inside it's bounds.
Default behavior seems to be that when the view's frame grows along the x axis, increasing frame.size.width, it always grows at the right edge, keeping the subviews fixed as if there were a fix left margin. However, when I want to expand the view on the left edge this doesn't work because of this behavior. In this case I want it to behave in a mirrored way, as if there were a fix right margin. I could of course "manually" move all subviews so it looks like that is the case, but that seems really awkward since there could be plenty of them.
So I guess the question really is if there is a way to shift a views bounds relative to it's subviews? Is maybe autoresizingMask the way to do this?
Thanks.
Maybe you should take a look at the AutoresizingMask property of a UIView subclass :-)
For example, if you have a UILabel called labelVideoTitle, you could set a mask like this :
[ labelVideoTitle setAutoresizingMask:UIViewAutoresizingFlexibleWidth ];
You can by the way add 2 mask at once like :
[ labelVideoTitle setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight ];
Good Luck !
Edit : To increase the parent view frame size at the left edge, you could change too its X position to the left to give the impression wanted ^^ For example if you add 10 pt to the width, try modifying the X origin -10 pt :-)
In interface builder, you can graphically indicate in the CMD-3 (little ruler icon) Size Inspector what each element in your view should do when the parent view is resized: you can indicate which borders (top, left, right, bottom), the given element should "stick to" when the parent view is resized. You can also indicate whether the given element should itself resize (in either width or height) or stay the same size. Underneath the hood, this sets the autoresize mask for the UIView element you're editing, but especially for making an element stick to a particular border, Interface Builder is the way to go.
IB Size Inspector also has a neat little animation that shows you the effect on a hypothetical element (little red square) during a resize, given your settings to the left.