TechChain
TechChain

Reputation: 8944

Issue in setting corner radius of view inside cell?

I have a custom UITableView cell.I am setting it's bottom left & bottom right corner radius.I am setting corner radius in cellForAtindexPath.Below is the code for that

if indexPath.row == 9 {
  recipeInfoCell.outerView.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 10)
  recipeInfoCell.layoutSubviews()
  recipeInfoCell.layoutIfNeeded()
} else  {
  recipeInfoCell.outerView.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 0)
  recipeInfoCell.layoutSubviews()
}

Now when i first launch the tableview then it does not set any corner radius. But when i scroll again then it is setting the corner radius.

I have created an extension of UIView in which there is one function which is setting the corner radius

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
}

Please tell how do i resolve this ?

Upvotes: 14

Views: 6663

Answers (8)

Moises Lozada
Moises Lozada

Reputation: 29

Based on my experience, this is what works for me to get the dynamic-sized layers work properly.

//Inside the cell or the view class
override func layoutSublayers(of layer: CALayer) {
    super.layoutSublayers(of: layer)
    
    if layer === self.layer {
        //Do any dynamic-layer update here
        myViewToRound.layer.cornerRadius = myViewToRound.bounds.width/2
    }
}

here, replace myViewToRound with the UIView subclass that you want to be rounded inside your cell/view

Upvotes: 0

arnaldogn
arnaldogn

Reputation: 61

This worked for me:

view.clipsToBounds = true
view.layer.cornerRadius = value
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]

Upvotes: 0

Mohit Kumar
Mohit Kumar

Reputation: 3086

I took reference from (https://stackoverflow.com/a/44067058/2781088) and modified the code and now it's working for me:

Add below code to your custom cell class:

enum cellStyle: Int {
    case Normal = 0, Rounded
}

class CustomTableCell:UITableViewCell {
    var cellType: Int = 0 {
        didSet {
            let maskLayer = CAShapeLayer()
            let cell: cellStyle = cellStyle(rawValue: cellType)!
            
            switch cell {
            case .Normal:
                let normal = UIBezierPath(roundedRect: self.viewMain.bounds, byRoundingCorners: [.bottomLeft, .bottomRight], cornerRadii: CGSize(width: 0, height: 0))
                
                maskLayer.path = normal.cgPath
                self.viewMain.layer.mask = maskLayer
            case .Rounded:
                let rounded = UIBezierPath(roundedRect: self.viewMain.bounds, byRoundingCorners: [.bottomLeft, .bottomRight], cornerRadii: CGSize(width: 10, height: 10))
                
                maskLayer.path = rounded.cgPath
                self.viewMain.layer.mask = maskLayer
            }
        }
    }
}

In your ViewController->cellForRowAt --- Call below code on Main Queue

DispatchQueue.main.async {
            if totalRows == index + 1 { //write your condition
                cell.cellType = cellStyle.Rounded.rawValue
            } else {
                cell.cellType = cellStyle.Normal.rawValue
            }
            cell.layoutSubviews()
            cell.layoutIfNeeded()
        }

Upvotes: 0

Himanshu
Himanshu

Reputation: 2832

Try writing these lines of code in layoutSubviews() of your custom UITableViewCell

override func layoutSubviews() {
  super.layoutSubviews()
  self.outerView.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 10)
}

Upvotes: 7

kerry
kerry

Reputation: 2582

I do not think it is a good idea to set corner radius in cellForRow atIndexPath. The reason being, this function is called many times during the lifetime of UITableView and you only need to set the corner radius only once and that too when the cell is initialised. Changing the corner radius based on indexPath will also affect the UITableView's performance.

A better way to this would be to create two cells, one with corner radius as 0 and another with 10 and the use those cells based on indexPath.

Then you can put your cornerRadius set logic in layoutSubview function in your custom cell.

If you want to do it in your tableView methods only, the correct way is to do it in willDisplayCell because after that call, cell's layoutSubviews function in called.

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if indexPath.row % 2 == 0 {
        let path = UIBezierPath(roundedRect: cell.contentView.bounds, byRoundingCorners: [.bottomRight, .bottomLeft], cornerRadii: CGSize(width: 10, height: 10))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        cell.contentView.layer.mask = mask
    } else {
        let path = UIBezierPath(roundedRect: cell.bounds, byRoundingCorners: [.bottomRight, .bottomLeft], cornerRadii: CGSize(width: 0, height: 0))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        cell.contentView.layer.mask = mask
    }
}

UPDATE: May 19, 2017

The above concept will work fine when the view that you want to round and put shadow on is the same size as the cell's content view. But if it is anything different than that, it won't work.

The reason for the above statement is that at the time when willDisplayCell is called, where the above code is using cell.contentView.bounds, the other views are not calculated yet. So when we will be using another view, we will have to use that view's bounds to calculate the mask's frame which we will be different from the actual one.

After reading up on this a bit, I found out that, to do this kind of a thing is by overriding draw(_ rect: CGRect) function of UITableViewCell. Because at this point, the view's size has been properly calculated and we can create a correct frame.

Below is the code from custom UITableViewCell class:

var shadowLayer = CAShapeLayer()

override func draw(_ rect: CGRect) {
    let path = UIBezierPath(roundedRect: self.outerView.bounds, byRoundingCorners: [.bottomRight, .bottomLeft], cornerRadii: CGSize(width: 10, height: 10))
    let mask = CAShapeLayer()
    mask.path = path.cgPath
    self.outerView.layer.mask = mask
    // Handle Cell reuse case        
    shadowLayer.removeFromSuperlayer()

    shadowLayer.shadowPath = path.cgPath
    shadowLayer.frame = self.outerView.layer.frame
    print(shadowLayer.frame)
    shadowLayer.shadowOffset = CGSize(width: 0, height: 0)
    shadowLayer.shadowColor = UIColor.black.cgColor
    shadowLayer.shadowOpacity = 0.9
    self.contentView.layer.insertSublayer(shadowLayer, below: self.outerView.layer)
    super.draw(rect)
}

Upvotes: 17

elk_cloner
elk_cloner

Reputation: 2149

You want a specific cell to round corner if i understand correctly. here is my solution.Though i am bit late but i try to help others. :) I just added my solution as a picture.

Step 1:

Created a custom Cell and did some necessary steps.

enter image description here enter image description here

Below is the code for Rounded bottom left and bottom right.Sorry for poor coding style:

enter image description here

Below is My view controller's configuration for showing tableView with rounded cells.

enter image description here

enter image description here

And Below is the moment of truth:

enter image description here

Upvotes: 2

Pradeep Kashyap
Pradeep Kashyap

Reputation: 961

put round corner code in main queue like this :

if indexPath.row == 9 { dispatch_async(dispatch_get_main_queue(),{
recipeInfoCell.outerView.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 10)
   })} else{
 dispatch_async(dispatch_get_main_queue(),{
recipeInfoCell.outerView.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 0)
}) }

Upvotes: 10

The iOSDev
The iOSDev

Reputation: 5267

For acquiring that in swift only I use to create one subclass of the UIButton like below

(in any .swift file of project)

//MARK: Custom Class for UIView
open class CustomView: UIView {
    open func drawViewsForRect(_ rect: CGRect) {
        fatalError("\(#function) must be overridden")
    }

    open func updateViewsForBoundsChange(_ bounds: CGRect) {
        fatalError("\(#function) must be overridden")
    }

}

Then define the below methods in same or deferent .swift file like this

    //MARK: - UIView Property Class
@IBDesignable open class CView : CustomView{

    @IBInspectable dynamic open var borderColor: UIColor = UIColor.clear{
        didSet{
            updateBorderColor()
        }
    }

    @IBInspectable dynamic open var borderWidth: CGFloat = 1.0{
        didSet{
            updateBorderWidth()
        }
    }

    @IBInspectable dynamic open var cornerRadius: CGFloat = 0.0{
        didSet{
            updateBorderRadius()
        }
    }

    @IBInspectable dynamic open var shadowColor: UIColor?{
        didSet{
            updateShadowColor()
        }
    }

    @IBInspectable dynamic open var shadowRadius: CGFloat = 0.0{
        didSet{
            updateShadowRadius()
        }
    }

    @IBInspectable dynamic open var shadowOpacity: Float = 0.0{
        didSet{
            updateShadowOpacity()
        }
    }

    @IBInspectable dynamic open var shadowOffSet: CGSize = CGSize(width: 0.0, height: 0.0){
        didSet{
            updateShadowOffset()
        }
    }

    //Update Borders Properties
    open func updateBorderColor(){
        self.layer.borderColor = borderColor.cgColor
    }
    open func updateBorderRadius(){
        self.layer.cornerRadius = cornerRadius
    }
    open func updateBorderWidth(){
        self.layer.borderWidth = borderWidth
    }

    //Update Shadow Properties
    open func updateShadowColor(){
        self.layer.shadowColor = shadowColor?.cgColor
        self.clipsToBounds = false;
        self.layer.masksToBounds = false;
    }
    open func updateShadowOpacity(){
        self.layer.shadowOpacity = shadowOpacity
        self.clipsToBounds = false;
        self.layer.masksToBounds = false;
    }
    open func updateShadowRadius(){
        self.layer.shadowRadius = shadowRadius
        self.clipsToBounds = false;
        self.layer.masksToBounds = false;
    }
    open func updateShadowOffset(){
        self.layer.shadowOffset = CGSize(width: shadowOffSet.width, height: shadowOffSet.height)
        self.layer.shadowColor = shadowColor?.cgColor
        self.clipsToBounds = false;
        self.layer.masksToBounds = false;
    }
}

Then just assign the CView class in storyboard at design time for any view controller and just provide the required values for the properties for that in side the attribute inspector for that view

In Storyboard

1) Class of the View Like this

set calls of view

2) Set property like this

Property like this

3) This will show view in side the design like this

view in storyboard

With this you even been able to see the shadow or corner radius directly in your design builder i.e. in side the storyboard view like the third image.

Upvotes: 3

Related Questions