This question already has an answer here:
iOS clipsToBounds YES and Shadow?
(1 answer)
Closed 5 years ago.
I need to design a view like card with rounded corner and shadow. I have one container view and inside that another view as like image I have attached. But when I am applying corner radius for the outer container view radius is getting set except the area which is having inner view. If I am making it clipToBounds = true then it is getting round all over but shadow is not coming. So plz help me out here.
Here is my code
containerView.layer.masksToBounds = false
containerView.layer.shadowColor = UIColor.black.cgColor
containerView.layer.shadowOpacity = 0.5
containerView.layer.shadowOffset = CGSize(width: -1, height: 1)
containerView.layer.shadowRadius = 5
containerView.backgroundColor = UIColor.white
containerView.layer.cornerRadius = 20
I am attaching Image below for for my issue. View Image
You have two views, a superview and its subview, and your goals are in conflict.
On the one hand, you want the corner radius of the superview to affect its subview. That can happen only if the superview masks to its bounds.
On the other hand, you want the superview's shadow to appear. That can happen only if the superview doesn't mask to its bounds.
So what you want is a logical impossibility.
The solution is easy. Use three views! Divide the job of shadow-making and clipping between two views.
The outermost view has the corner radius and the shadow and doesn't mask to bounds. This is the shadow-maker.
The next view is its subview. It is exactly the same size, and it also has the corner radius and it does mask to bounds. This is the clipper.
The next view is the content, the subview(s) of the subview. It will be masked by the second view so the corner radius will affect it.
Related
I am facing a strange issue when I try to apply rounded corners and shadow to my UITableView with dynamic content, which changes height as per the data (number of cells).
Here is screen recording of the jerky effect on scrolling.
My motive is to add shadow and corner radius to the dynamic table view.
When I add the corner radius it works fine, but when I try to add the shadow it doesn't show up. So I found a solution here saying that we need to set
self.tableView.clipsToBounds = false
self.tableView.layer.masksToBounds = false
However, after setting it I am getting the above jerky effect and the corner radius is no longer visible.
I tried other (proposed) solutions like adding a custom view on runtime with respect to tableview frame, but that creates a static height view and hence disabling the interactivity with the superview for that area.
Here is my code:
searchResultTblView.layer.cornerRadius = 10
searchResultTblView.layer.maskedCorners = [.layerMinXMaxYCorner , .layerMaxXMaxYCorner]
searchResultTblView.keyboardDismissMode = .onDrag
searchResultTblView.layer.shadowColor = UIColor.gray.cgColor
searchResultTblView.layer.shadowOpacity = 0.6
searchResultTblView.layer.shadowRadius = 5
searchResultTblView.tableFooterView = UIView.init(frame: CGRect.zero)
searchResultTblView.separatorInset = .zero
searchResultTblView.clipsToBounds = false
searchResultTblView.layer.masksToBounds = false
I know there's probably a better way to set both shadow and corner radius on the table view. However, I am currently unable to achieve it.
Multiple placed in my app, I have views with both shadows and corner radii. I tried adding a new view, and suddenly the code I was reusing doesn't work anymore. I can only set a corner radius or a shadow, depending on what I put for masksToBounds. Here's the code I use for both the faulty view and my other views:
itemCountLabel.layer.masksToBounds = false
itemCountLabel.layer.cornerRadius = itemCountLabelSize / 2.0
itemCountLabel.layer.shadowColor = UIColor.black.cgColor
itemCountLabel.layer.shadowOpacity = 0.25
itemCountLabel.layer.shadowRadius = 5
itemCountLabel.layer.shadowOffset = CGSize(width: 4, height: 4)
contentView.addSubview(itemCountLabel)
It's not possible to implement as you've tried. Shadow is always applied outside the bounds of the UIView and the cornerRadius will not be visible without masking the bounds of UIView. So, better add a UIView behind the UILabel and to reuse the function write an extension of UIView that returns a UIView contains the view you want to apply the shadow.
Here you need to use two different views one to round the corners and the other behind it to show the shadow, As both these properties don’t work together because of the Mask To Bounds and Clip To Bounds features. As corner radius needs to clip the edges which might can contain the shadow.
So to have both of the things use a shadow view behind the view which you want to have rounded corners.
Hi I want to create a view which looks something like below -
What I tried so far is I put two properties called image and text inside an UIView and tried to initialise the image view and text field and added in UIView I am able to see the image but not the text. May be I am missing proper constraints to put or something else. Can please someone help on what should be the best approach for this.
I would prefer it to be done without much involvement of storyboard i.e setting constraints for different views using storyboard. I am fine setting constraints in code. As you see it is a fairly reusable element and hence I want it as an class which can be assigned to any uiview and that should be it. Please correct me if something can be done better?
Thanks & Regards
Assumptions: the entire picture provided above is contained in a UIView and we are using Storyboard.
Refer to this picture for clarity in the explanation.
Make sure the outermost View has enough constraits so AutoLayout can properly size the view. If this doesn't happen, nothing past this point will matter.
Create 2 Views. Each has the same width as the container and is half the height. Place one in the upper half and one in the lower half. These are the ones with the blue and orange backgrounds.
Add an ImageView to the top View (blue background). Make the ImageView half the height of its Superview. Make the ImageView centered vertically in container. Add a constraint for the leading edge of the ImageView to be the same as the leading edge of the Superview margin. Add an AspectRatio constraint to the ImageView of 1:1.
Add a TextField to the top View (blue background). Center the TextField vertically in the container. Add a constraint for the HorizontalSpace between TextField leading and ImageView trailing and make the constant 8. Add a constraint for the TrailingSpace to the TextView for the SuperView trailing margin.
Change the Placeholder Text for the TextField to "Email"
Place the image in the imageview.
Repeat for the orange background View.
For the bottom borders, use this:
extension UIView{
func addBottomBorder(borderThickness: CGFloat, color: UIColor , widthPct: CGFloat) {
let border = UIView()
border.backgroundColor = color
border.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
var x: CGFloat = 0
let width = self.frame.size.width * widthPct
if widthPct < 1{
x = (self.frame.size.width - width) / 2
}
border.frame = CGRect(x: x, y: self.frame.size.height - borderThickness, width: width, height: borderThickness);
self.addSubview(border)
}
}
I am trying to add shadow to an UIImage that is inside a UITableViewCell. I configured image using UIBezierPath to draw a shadow around its bounds. The shadow appear on either side of the image but the shadow on the top of image is clipped by the table view cell's border because there is no space between image's top and the table cell's border. I cannot have space between image's top and border because these cells are collapsible and will clip lower parts of the image to make it look like a carousel of cards stacked one on the top of another. Here is the code I am using to generate shadows:
let shadowPath = UIBezierPath(rect: self.bounds)
layer.masksToBounds = false
self.clipsToBounds = false
layer.shadowColor = UIColor.blackColor().CGColor
layer.shadowOffset = CGSizeMake(0.0, -3.0)
layer.shadowOpacity = 0.1
layer.shadowRadius = 0.56
layer.shadowPath = shadowPath.CGPath
Here is a screenshot of what I'm trying to achieve:
What I've done so far is dynamically load table cells with images of cards, hide overflow of image that are greater than the height of the cells, increase height of cells on tap so that the tapped card pushes other cards below it by increasing the height of the cell and come to full view mode.
The problem is the shadows on top of the card gets hidden because maybe it tresspasses into the cell above it?
I understand that my entire approach might be wrong on this one, so I'm open to any suggestions. Please help.
UPDATE
https://github.com/gleue/TGLStackedViewController -> TGLStackedViewController
This view controller is exactly what I want plus shadows on top. Now I've come to realize that I do not need to render shadows while viewing but adding shadows while saving image will do. It is more efficient as well because the app does not have to draw the shadows every time the image is being displayed The problem however, is, this library is written in ObjC and is compatible with iOS9+ only.
I have a static image with an area on it that is meant to display some multi-line text. Like a speech bubble from a character in a video game. I grabbed a stock image that looks like this:
I have a UIImageView set up to be aspect-fit (see this question) inside of a subview of the main view. I have a UILabel also set up inside of the subview, which will hold the multi-line text. I want to be able to move the subview around the screen and have it be any size and still have the UIImageView stay the same aspect and have the UILabel fit inside the bubble of the image.
I made a sample project which has this set up already.
The way I intend to keep the UILabel's bounds inside the speech bubble area is by setting up constraints that are proportional to the center x and y of the UIImageView. For my image, the left edge multiplier is 0.65, the right is 1.8, the top is 0.19 and the bottom is 0.63.
I wrote a couple functions that extend from UIView to confirm this:
/**
Draws a vertical line proportional to the center x of the view.
A `proportional` value of 0 is the left edge, while 2 is the right edge.
:param: proportion The value from the left edge (0.0) to the right edge (2.0)
:param: inColor The color to draw the line in (red by default)
*/
func drawVertLineAtProportion(proportion: CGFloat, inColor: UIColor = UIColor.redColor()) {
let size = self.frame.size
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
let x = self.bounds.origin.x + proportion*self.frame.size.width/2
var vertPath = UIBezierPath()
vertPath.lineWidth = size.height/150.0
vertPath.moveToPoint(CGPointMake(x, 0))
vertPath.addLineToPoint(CGPointMake(x, self.frame.size.height))
inColor.setStroke()
vertPath.stroke()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.addSubview(UIImageView(image: image))
}
And drawHorizLineAtProportion is similar, but for horizontal lines.
I can confirm that my multiplier values are correct by running these 4 lines in viewDidAppear:
imageView.drawVertLineAtProportion(0.65)
imageView.drawVertLineAtProportion(1.8)
imageView.drawHorizLineAtProportion(0.19)
imageView.drawHorizLineAtProportion(0.63)
And then the imageView looks like this:
I can change the size of the subView that contains the imageView to any size I want, and the imageView stays aspect-fit and the red box in the intersection of those red lines is always exactly what I want.
So how come when I set up the constraints of the edges of the UILabel to follow the same formula the edges don't line up?
It seems that when the width of the imageView is maxed the left and right edges are correct but the top and bottom are wrong:
And if the height is maxed, then the top and bottom are correct, but the left and right are wrong:
So how come the UILabel bounds aren't lining up with the red lines?
EDIT to specify that I know there is a runtime fix, but I want to know why storyboard doesn't work.
If I add these lines in viewDidAppear to fix the frame of the UILabel, it works:
let upperLeft: CGPoint = CGPointMake(imageView.frame.origin.x + 0.65*imageView.frame.size.width/2, imageView.frame.origin.y + 0.19*imageView.frame.size.height/2)
let lowerRight: CGPoint = CGPointMake(imageView.frame.origin.x + 1.8*imageView.frame.size.width/2, imageView.frame.origin.y + 0.63*imageView.frame.size.height/2)
let size: CGSize = CGSizeMake(lowerRight.x - upperLeft.x, lowerRight.y - upperLeft.y)
speechLabel.frame = CGRectMake(upperLeft.x, upperLeft.y, size.width, size.height)
But I still want to know why what I have set in storyboard doesn't work.
I figured out a solution, fully in storyboard, that gets me what I want, although I do believe my original question shows that there is a bug in Xcode.
I was noticing that since Xcode didn't seem to respect that I wanted to align speechLabel to the sister-UI element imageView, I just made the UIView I was aligning to no longer the imageView.
The solution I implemented was to put the imageView and speechLabel into a new parent view, which I call aspectFitView. All of the constraints that I put on the imageView to make it aspect-fit, I put on aspectFitView instead.
Then I set the constraints of imageView to make it the size of aspectFitView: align center x and y and equal width and height.
Now that the parent of speechLabel is the exact size, position, and aspect ratio of the imageView, I set up my proportional constraints for the speechLabel against the aspectFitView instead, and voila. It all works beautifully. It looks correct on my storyboard, I don't have to add any post-viewDidAppear frame-correcting code, and it works perfectly on any device I run it on.
I updated the sample project (Xcode 6.4) with the solution for anyone interested.