I'm trying to round only the top top corners (TopLeft and TopRight) of a view and imageview. With the code below I was able to round up only the left corner (although there is also specified TopRight). Maybe he doesn't see the constraint placed on the storyboard.
How do I fix?
Thank you.
let rectShape = CAShapeLayer()
rectShape.bounds = self.CopertinaImage1.frame
rectShape.position = self.CopertinaImage1.center
rectShape.path = UIBezierPath(roundedRect: self.CopertinaImage1.bounds, byRoundingCorners: [.TopRight, .TopLeft], cornerRadii: CGSize(width: 10, height: 10)).CGPath
self.CopertinaImage1.layer.backgroundColor = UIColor.greenColor().CGColor
self.CopertinaImage1.layer.mask = rectShape
The right corner is always the same:
Edit:
I've tried placing the following code in viewDidLayoutSubviews:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let rectShape = CAShapeLayer()
rectShape.bounds = self.CopertinaImage1.frame
rectShape.position = self.CopertinaImage1.center
rectShape.path = UIBezierPath(roundedRect: self.CopertinaImage1.bounds, byRoundingCorners: [.TopRight, .TopLeft], cornerRadii: CGSize(width: 10, height: 10)).CGPath
self.CopertinaImage1.layer.backgroundColor = UIColor.greenColor().CGColor
self.CopertinaImage1.layer.mask = rectShape
}
But it didn't work.
I've tried the code you've posted. For whatever reason when I enter your constraints and recreate your view hierarch, the auto layout engine does not call viewDidLayoutSubviews after it does it's second pass. I could force it to but calling self.view.layoutIfNeeded() from viewWillAppear. For example:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let rectShape = CAShapeLayer()
rectShape.frame = self.imageView.bounds
rectShape.path = UIBezierPath(roundedRect: self.imageView.bounds, byRoundingCorners: [.TopRight, .TopLeft], cornerRadii: CGSize(width: 10, height: 10)).CGPath
self.imageView.layer.backgroundColor = UIColor.greenColor().CGColor
self.imageView.layer.mask = rectShape
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutIfNeeded()
}
Results in:
With both corners rounded.
I would say a better, more elegant solution that avoids this auto layout glitch is to just set the corner radius of the image view container, as it seems like that what you're doing anyways. For example:
override func viewDidLoad() {
super.viewDidLoad()
self.imageView.clipsToBounds = true
self.imageContainerView.layer.cornerRadius = 10.0
self.imageContainerView.layer.masksToBounds = true
}
You can also do this from viewDidLoad because it doesn't depend on any of your views' frames being set.
I just tried your code in a playground. Worked fine for me. Maybe I am misunderstanding the problem.
Related
I'm currently trying to make a dotted border around my UIView. I referred to a previous post: Dashed line border around UIView
The left side of the UIView gets the red dotted lines but the right side misses the edge.
This is left side
This is right side
Here is my code which I am executing in viewDidLoad:
myview.backgroundColor = UIColor.lightGray
myview.layer.cornerRadius = 4
let dottedBorder = CAShapeLayer()
dottedBorder.strokeColor = UIColor.red.cgColor
dottedBorder.lineDashPattern = [4, 4]
dottedBorder.frame = myview.bounds
dottedBorder.fillColor = nil
dottedBorder.path = UIBezierPath(roundedRect: myview.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 4, height: 4)).cgPath
myview.layer.addSublayer(dottedBorder)
Assuming you're using autolayout, you shouldn't depend on the view size inside viewDidLoad, because usually it equals you storyboard device size(selected is SB editor), not the real device one.
Anyway, it may change in future. All calculations depending on frame/bounds needs to be done inside viewDidLayoutSubviews. Something like this:
private let dottedBorder = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
dottedBorder.strokeColor = UIColor.red.cgColor
dottedBorder.lineDashPattern = [4, 4]
dottedBorder.fillColor = nil
myview.layer.addSublayer(dottedBorder)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
dottedBorder.frame = myview.bounds
dottedBorder.path = UIBezierPath(roundedRect: myview.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 4, height: 4)).cgPath
}
I'm trying to round the top and left corners but know how to do it for iOS 11+ (cause of the easiest new feature) but can't do that for iOS 9-, could be cool if u guys could help me with that :)
Here's an image of how it looks like now without rounding -
for example for iOS 11+, I do it like this -
layer.maskedCorners = [
.layerMinXMaxYCorner,
.layerMaxXMaxYCorner
]
layer.cornerCurve = .continuous
Make a UIView extension and add the below method in it:
extension UIView {
func roundCorners(corners: UIRectCorner, radius: CGFloat) {
DispatchQueue.main.async {
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.frame = self.bounds
mask.path = path.cgPath
self.layer.mask = mask
}
}
}
And then you can round corner radius of any views from any sides.
Example:
anyView.roundCorners(corners: [.topRight, .bottomLeft, .bottomRight], radius: 20)
Note: Don't forget to set the clipsToBounds property of the view to true before using this method. Happy Coding :)
Write this method and put it wherever you need, for example in viewDidLoad:
private let cornerRadius: CGFloat = 10
private func setMaskLayers() {
if #available(iOS 11.0, *) {
yourView.clipsToBounds = true
yourView.layer.cornerRadius = cornerRadius
yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
let path = UIBezierPath(roundedRect: yourView.bounds, byRoundingCorners: [.topRight, .topLeft], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
yourView.layer.mask = maskLayer
}
}
im trying to make rounded corners for the sub view in the top left and top right using the following code
maskLayer.path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.topRight, .topLeft], cornerRadii: CGSize(width: 10, height: 10)).cgPath
also tried in viewDidLayoutSubviews(), but only left border radius is working... the right border radius is not working.
can someone help me out
You may miss configure something , here maskedCorners rounds top left && right --- and roundCorners rounds bottom left && right
class ViewController: UIViewController {
let redBox = UIView(frame: CGRect(x: 100, y: 100, width: 128, height: 128))
let blueBox = UIView(frame: CGRect(x: 100, y: 300, width: 128, height: 128))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
redBox.backgroundColor = .red
redBox.layer.cornerRadius = 25
redBox.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
view.addSubview(redBox)
blueBox.backgroundColor = .blue
view.addSubview(blueBox)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
blueBox.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 25.0)
}
}
extension UIView {
func roundCorners(corners: UIRectCorner, radius: CGFloat) {
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
That often happens when your maskLayer is wider than the view it masks.
See the diagram below. Overlapped area is a rectangle with one corner rounded.
try this
override func layoutSubviews() {
super.layoutSubviews()
yourView.roundCorners(topLeft: 25, topRight: 25)
bookImage.clipsToBounds = true
}
I'm trying to round the corners on the UIView in bottom left and bottom right.
extension UIView {
func roundBottom(raduis: CGFloat){
let maskPath1 = UIBezierPath(roundedRect: bounds,
byRoundingCorners: [.BottomRight, .BottomLeft],
cornerRadii: CGSize(width: raduis, height: raduis))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = bounds
maskLayer1.path = maskPath1.CGPath
layer.mask = maskLayer1
}
}
And call cell.bottomCorner.roundBottom(8)
But I get it:
iPhone 5:
iPhone 6s:
iPhone 6s Plus:
You have to update the mask everytime the view changes its size, therefore ideally you should change it whenever UIView.layoutSubviews is called:
override func layoutSubviews() {
super.layoutSubviews();
// update mask
}
It's not ideal to do it in an extension helper method. You should create a specific class to handle the size change.
I have three buttons in which I want to round:
Top corners of first button
Bottom corners of second button
All four corners of third button
I achieved this by the following code:
button1.roundedButton1()
button2.roundedButton2()
button3.layer.cornerRadius = 5
extension UIButton {
func roundedButton1(){
let maskPAth1 = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.topLeft , .topRight],
cornerRadii:CGSize(width:5.0, height:5.0))
let maskLayer1 = CAShapeLayer()
// maskLayer1.frame = self.bounds
maskLayer1.path = maskPAth1.cgPath
self.layer.mask = maskLayer1
}
func roundedButton2(){
let maskPAth1 = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.bottomLeft , .bottomRight],
cornerRadii:CGSize(width:5.0, height:5.0))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = self.bounds
maskLayer1.path = maskPAth1.cgPath
self.layer.mask = maskLayer1
}
}
But the width of my 1st and 2nd button is getting disturbed.
If I use button2.layer.cornerRadius = 5 then the width becomes alright. I have searched which code it altering its width but didn't find anything appropriate. And this is the only working solution i found for UIButton.Can anyone tell me why the width of button is changing and how to fix it?
The constraints of button are as follows:
Instead of using UIButton you can achieve this result by using UIView & UITapGestureRecognizer
add this extension
extension UIView {
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
}
}
then create a custom view class
class CustomView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
self.roundCorners([.topRight, .topLeft], radius: 5) // you can use .topRight , .topLeft , .bottomRight, .bottomLeft
}
}
then use the class on your view
Hope this will help you