问题
I'm trying to figure out how to mask a UIView to display a shape that represents a Square with half of a circle sitting ontop of it.
Basically, it would be like applying a border radius to only the top two corners to round them out and create an arc.
I'm using swift, and this is driving me crazy.
EDIT: CornerRadius does not do what I want. I feel like I'll have to use a CGShapeLayer but I'm not sure how to create the arc.
EDIT2:
EDIT3:
Considering explaining what I want, providing pictures, and explaining that I didn't understand what I needed to do, or how I needed to do it isn't enough for some people, here's what I've tried:
Attempted setting the layer.cornerRadius
property, while pushing the bottom of the view out past the screen to hide the bottom corners from appearing cut off, this type of masking was too circular and did not provide the proper arc results.
I've attempted using UIBezierPath
setting the top two corners to use a cornerRadii of the views width / 2
. This also did not yield proper results. Upon attempting to hardcode values into the cornerRadii, I noticed that regardless of the value, I could not seem to obtain the results that I had wanted.
What other options are there? Or am I just using the BezierPath incorrectly?
EDIT 4:
Here is my original code.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let bezierPath = UIBezierPath(roundedRect: navBarView.bounds, byRoundingCorners: [.TopLeft, .TopRight], cornerRadii: CGSizeMake(navBarView.bounds.width / 2, navBarView.bounds.width / 2)).CGPath;
let shapeLayer = CAShapeLayer();
shapeLayer.bounds = navBarView.bounds
shapeLayer.path = bezierPath
navBarView.layer.mask = shapeLayer
updateUI() // <-- Label text, etc.
}
Also, as stated before, had attempted doing navBarView.layer.cornerRadius = x
回答1:
Try this:
let path = UIBezierPath(roundedRect:self.view.bounds, byRoundingCorners:[.TopLeft, .TopRight], cornerRadii: CGSizeMake(20, 20))
let maskLayer = CAShapeLayer()
maskLayer.frame = self.view.bounds;
maskLayer.path = path.CGPath
self.view.layer.mask = maskLayer;
The UIBezierPath
class lets you define a path consisting of straight and curved line segments and render that path in your custom views.
- Generate rounded corners of the path in UIBezierPath
- To generate a CAShapeLayer, it is set to one of the path
- layer to the view that you want to on one side only rounded corners 2 as the mask layer
Example:
let DynamicView=UIView(frame: CGRectMake(50, 200, 200, 300))
DynamicView.backgroundColor=UIColor.redColor()
let path = UIBezierPath(roundedRect:DynamicView.bounds, byRoundingCorners:[.TopLeft, .TopRight], cornerRadii: CGSizeMake(20, 20))
let maskLayer = CAShapeLayer()
maskLayer.frame = DynamicView.bounds;
maskLayer.path = path.CGPath
DynamicView.layer.mask = maskLayer;
self.view.addSubview(DynamicView)
Result:
回答2:
Is not that difficult, you need to add mask to the layer of the view. Pay attention that adding the mask will cut everything out of the mask itself.
The other thing you should pay attention is that if your view is resizing also you mask need to be adapted, since it doesn't update automatically the path.
Imagining that you are using SWIFT, you can subclass UIView:
override func layoutSubviews() {
super.layoutSubviews()
maskLayer.frame = layer.bounds
maskLayer.path = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.TopLeft, .TopRight], cornerRadii: CGSizeMake(bounds.size.widht/2, bounds.size.widht/2)).CGPath
}
override init(frame: CGRect) {
super.init(frame: frame)
layer.mask = maskLayer
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
layer.mask = maskLayer
}
maskLayer
is a CAShapeLayer
that you must create as a constant or as a lazy property.
The code is not tested.
回答3:
Hi I had same issue I rounded corners after view is resized by dispatching the queue
self.lblShortDescription.frame = CGRectMake(self.lblShortDescription.frame.origin.x, self.lblShortDescription.frame.origin.y, width, self.lblShortDescription.requiredHeight()) // here required height is the function to resize the label according to the content.
dispatch_async(dispatch_get_main_queue(), {
self.viewTopTitaleBack.round([UIRectCorner.TopLeft, UIRectCorner.TopRight], radius: 8.0) // here rounding is again a function I used for rounding the view by passing the parameters.
});
extension UILabel{
func requiredHeight() -> CGFloat{
let label:UILabel = UILabel(frame: CGRectMake(0, 0, self.frame.width, CGFloat.max))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
label.font = self.font
label.text = self.text
label.sizeToFit()
return label.frame.height
}
}
extension UIView {
func round(corners: UIRectCorner, radius: CGFloat) -> CAShapeLayer {
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.CGPath
self.layer.mask = mask
return mask
}
}
Hope this helps.
回答4:
Tested and tried Ramesh_T's answer, it works perfectly with the addition of .clipsToBounds made true. Works like a charm, especially if nested into an extension function such as:
(SWIFT 3)
extension UIView {
func roundCorners(_ corner: UIRectCorner,_ radii: CGFloat) {
let maskLayer = CAShapeLayer()
maskLayer.frame = self.layer.bounds
maskLayer.path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corner, cornerRadii: CGSize(width: radii, height: radii)).cgPath
self.layer.mask = maskLayer
layer.masksToBounds = true
}
}
Called like this to round a specific views top left and right corners:
someView.roundCorners([.topRight,.topLeft], 60)
EDIT
However, looking at the method declaration for UIBezierPath(roundedRect..) : it seems the corner radii is maxed out to either half the view's height or width, whichever is smaller...
来源:https://stackoverflow.com/questions/34878220/round-out-the-top-right-and-left-corners-of-a-view