I'm new to iOS development and trying to create a custom UITableViewCell that has a background which top-left and bottom-left corners I want to round, but it only works for the top-left corner.
Here is my code (Swift 3):
override func awakeFromNib() {
super.awakeFromNib()
let bgViewMask = CAShapeLayer()
let bgViewPath = UIBezierPath(
roundedRect: bgView.bounds,
byRoundingCorners: [.topLeft, .bottomLeft],
cornerRadii: CGSize(width: 8, height: 8)
)
bgViewMask.frame = bgView.bounds
bgViewMask.path = bgViewPath.cgPath
bgView.layer.mask = bgViewMask
}
I also tried this in layoutSubviews()
override func layoutSubviews() {
super.layoutSubviews()
//code here
}
but it didn't work as well.
If I use bgView.layer.cornerRadius = 8 it works for all corners.
Am I doing anything wrong?
You can try this:
import UIKit
class CustomImageView: UIImageView {
override public func layoutSubviews() {
super.layoutSubviews()
self.roundUpCorners([.topLeft, .bottomLeft], radius: 8)
}
}
extension UIView {
func roundUpCorners(_ 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
}
}
Related
I have a tablviewcell which has uiview in it.
Based on some logic I change the background color and make the left and right corner rounded.
I make these view corner round from cellForRowat indexPath function.
Here is my extension.
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
}
}
and how I use it
cell?.myCustomView.roundCorners(corners: [.bottomRight,.bottomLeft], radius: 10.0)
Its working fine when width of iphones is 375,
but it fails to update for device with width greater than 375.
And after scrolling the tableview, it again stretches back correctly to the desired width.
How to solve this problem ?
You want to update the path when the view changes size. In cellForRowAt the cell has not yet been fully laid-out by auto-layout.
So...
Create a UIView subclass for your "rounded corners" view (simple example):
class RoundedCornersView: UIView {
var corners: UIRectCorner = UIRectCorner()
var radius: CGFloat = 0.0
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
Now, anytime the view changes size - such as on first use or device rotation, for example - the view will automatically update the path.
Here is how you would use it in a table cell. In Storyboard, set the class of the "background view" to RoundedCornersView
class RoundedCornersView: UIView {
var corners: UIRectCorner = UIRectCorner()
var radius: CGFloat = 0.0
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
class MyTestCell: UITableViewCell {
#IBOutlet var myCustomView: RoundedCornersView!
}
Then, in cellForRowAt:
let cell = tableView.dequeueReusableCell(withIdentifier: "MyTestCell", for: indexPath) as! MyTestCell
if shouldBeRounded {
cell.myCustomView.corners = [.bottomRight, .bottomLeft]
cell.myCustomView.radius = 10.0
cell.myCustomView.backgroundColor = .green
} else {
cell.myCustomView.corners = []
cell.myCustomView.radius = 0.0
cell.myCustomView.backgroundColor = .white
}
return cell
Use this:
override func draw(_ rect: CGRect) {
super.draw(rect)
selectedView.roundCorners(corners: [.topLeft, .topRight], radius: 12.0)
}
I have a UIImageView inside of a UICollectionView Cell and I wanna make it' s top corners rounded. I couldn' t solve on my on, any help is appreciated.
You can see cell has 10px corner radius and I want same effect to be applied on image too.
You can try UIRectCorner Document here
here my custom class AGRoundCornersView
extension UIImageView {
public func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
let maskPath = UIBezierPath(roundedRect: bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius))
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
layer.mask = shape
}
}
code :
1. Call When View is resized.
uiimage.roundCorners([.topLeft, .topRight], radius: 10)
2. Create custom class
class CustomImageView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
self.roundCorners([.topLeft, .topRight], radius: 10)
}
}
Swift 5
In your collectionview cell, put the below two lines of code & you are good to go. No need to write any extension:
cell._imageView.layer.cornerRadius = 10
cell._imageView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
Here corner radius will apply TopLeft & TopRight corners & rest of corners will remain same.
If you want to apply the corner radius in BottomLeft & BottomRight corners, then maskedCorners should look like the below:
cell._imageView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
Hope it helps.
Try this in your UICollectionViewCell
override func awakeFromNib() {
super.awakeFromNib()
DispatchQueue.main.async {
self.image.roundCorners([.topRight,.topLeft], radius: 8)
self.image.layer.masksToBounds = true
}
}
Thanks to AshvinGudaliya and Sujewan
Following code worked for my collection cell's ImageView Top Left-Right corners
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell:TUGBucketCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! TUGBucketCell
//Rounded corner ImageView
DispatchQueue.main.async {
cell.imgVw.roundCorners([.topLeft, .topRight], radius: 10)
cell.imgVw.layer.masksToBounds = true
}
return cell
}
and the extension is
extension UIImageView {
public func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
let maskPath = UIBezierPath(roundedRect: bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius))
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
layer.mask = shape
}
}
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
I want only the bottom border of my imageView to be rounded. How do I do that programmatically?
Thanks for your help!
You can do this by sub-classing UIImageView:
import UIKit
class CustomImageView: UIImageView {
override public func layoutSubviews() {
super.layoutSubviews()
self.roundUpCorners([.bottomLeft, .bottomRight], radius: 30)
}
}
extension UIView {
func roundUpCorners(_ 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
}
}
The result will be something like this:
I know I can round all four corners using:
myBtn.layer.cornerRadius = 8
myBtn.layer.masksToBounds = true
Since I only want to round two, I did some research and found this:
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
}
}
Which is called like this:
view.roundCorners([.TopLeft , .TopRight], radius: 10)
Yet this doesn't work for a UIButton. When I switch the extension to be for type UIButton and pass it a button , the output looks like this:
The question is, how do I adapt this to work on a UIButton?
Swift 4: For latest iOS 11 onwards
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 11.0, *) {
self.viewToRound.clipsToBounds = true
viewToRound.layer.cornerRadius = 20
viewToRound.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
// Fallback on earlier versions
}
}
Earlier iOS (10,9 etc) Versions (works for iOS 11 too)
override func viewDidLayoutSubviews() {
self.viewToRound.clipsToBounds = true
let path = UIBezierPath(roundedRect: viewToRound.bounds,
byRoundingCorners: [.topRight, .topLeft],
cornerRadii: CGSize(width: 20, height: 20))
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
viewToRound.layer.mask = maskLayer
}
Adding Extension of UIButton:
extension UIButton{
func roundedButton(){
let maskPath1 = UIBezierPath(roundedRect: bounds,
byRoundingCorners: [.topLeft , .topRight],
cornerRadii: CGSize(width: 8, height: 8))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = bounds
maskLayer1.path = maskPath1.cgPath
layer.mask = maskLayer1
}
}
Calling in viewDidAppear/viewDidLayoutSubviews:
btnCorner.roundedButton()
Button Corner OutPut:
For swift 3 Kirit Modi's answer is changed to:
extension UIButton {
func roundedButton(){
let maskPAth1 = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.topLeft , .topRight],
cornerRadii:CGSize(width:8.0, height:8.0))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = self.bounds
maskLayer1.path = maskPAth1.cgPath
self.layer.mask = maskLayer1
}
}
At the start of the extension's file don't forget to add:
import UIKit
If you want an extension for a UIView with the option of rounding top or bottom corners you can use:
extension UIView {
func roundedCorners(top: Bool){
let corners:UIRectCorner = (top ? [.topLeft , .topRight] : [.bottomRight , .bottomLeft])
let maskPAth1 = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: corners,
cornerRadii:CGSize(width:8.0, height:8.0))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = self.bounds
maskLayer1.path = maskPAth1.cgPath
self.layer.mask = maskLayer1
}
}
Which is called for a button as:
myButton.roundedCorners(top: true)
iOS 11 has made it really easy to round corners. The code below rounds the top left and bottom right corners.
myView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner]
For swift 5 and the most flexibility
Define an extension with a roundCorners function
extension UIButton {
func roundCorners(corners: UIRectCorner, radius: Int = 8) {
let maskPath1 = UIBezierPath(roundedRect: bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius))
let maskLayer1 = CAShapeLayer()
maskLayer1.frame = bounds
maskLayer1.path = maskPath1.cgPath
layer.mask = maskLayer1
}
}
Call the roundCorners function
myButton.roundCorners(corners: [.topLeft, .topRight])
Or with a specific radius
myButton.roundCorners(corners: [.topLeft, .topRight], radius: 20)
Update you extension to be like this:
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()
let rect = self.bounds
mask.frame = rect
mask.path = path.cgPath
self.layer.mask = mask
}
}
The shape layer (mask) needs to know the frame
Use this Code,
let path = UIBezierPath(roundedRect:viewTo.bounds, byRoundingCorners:[.TopRight, .TopLeft], cornerRadii: CGSizeMake(20, 20))
let maskLayer = CAShapeLayer()
maskLayer.path = path.CGPath
viewTo.layer.mask = maskLayer
hope its helpful
That's what helped me
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
}
}
Pay attention to the fact that if you have layout constraints attached to it, you must refresh this as follows in your UIView subclass:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
yourButtonOutletName.roundCorners(
corners: [.topLeft, .topRight],
radius: yourButtonOutletName.frame.height / 2
)
}
You forgot to set the frame of your shape layer:
mask.frame = layer.bounds
rounding is applied to corner's of view/Button .. , but coming to border of button , it is not applying correctly. can I have any solution for that? #
Here is the code that I have used , which is working (border) in iOS11.0 and above and not in below versions(<11.0)
if #available(iOS 11.0, *) {
self.layer.cornerRadius = radius
self.layer.maskedCorners = maskedCorners
} else {
let shapeLayer = CAShapeLayer()
shapeLayer.position = self.center
self.layer.masksToBounds = true
self.clipsToBounds = true
let bezirePath = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius))
shapeLayer.bounds = frame
shapeLayer.path = bezirePath.cgPath
self.layer.mask = shapeLayer