cyclingIsBetter
cyclingIsBetter

Reputation: 17591

iOS: subclass UIView in a tableview row

I am trying to set this custom view in every single row of my tableview. In my interface builder I add a UIView in the UITableViewCell and I link the IBOutlet. So I subclass it to CircleView

this is its class

class CircleView: UIView {

    var shapeLayer = CAShapeLayer()

    override func draw(_ rect: CGRect) {
        super.draw(rect)

        let circlePath = UIBezierPath(arcCenter: CGPoint(x: rect.width/2,y: rect.height/2), radius: CGFloat(20), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi*2), clockwise: true)
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = Constants.defaultCircleFillColor
        shapeLayer.strokeColor = UIColor.clear.cgColor
        shapeLayer.lineWidth = 1.5
        self.layer.addSublayer(shapeLayer)
    }

    func setColor(_ color: UIColor) {
        shapeLayer.strokeColor = color.cgColor
    }
}

but when I try to call the setColor func in cellForRowAt delegate, nothing happen. Is there a way to manage its stroke color?

this is the cellForRowAt:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let remote_row: RemoteType = sections[indexPath.section].cellData[indexPath.row]

        if let cell = tableView.dequeueReusableCell(withIdentifier: remote_row.identifier) as? RemoteCell {
            switch remote_row {
            case .Item(circleColor: let color, text: let title):
                cell.configCell(circleColor: color, description: title)
            }
            return cell
        } else {
            fatalError("Unknown identifier")
        }
    }

and the RemoteCell:

class RemoteCell: UITableViewCell {

    @IBOutlet weak var title: UILabel!
    @IBOutlet weak var circleView: CircleView!
    @IBOutlet weak var settingButton: UIButton!

    func configCell(circleColor: UIColor, description: String) {
        title.text = description
        circleView.setColor(UIColor.blue)
    }
}

Upvotes: 0

Views: 108

Answers (2)

Hakim
Hakim

Reputation: 1326

the draw method is going to get executed when your tableView draws the cell, so the color change is going to get overriden and set to clear.

try something like this:

class CircleView: UIView {

    var shapeLayer = CAShapeLayer()
    var myColor:UIColor = UIColor.clear

    override func draw(_ rect: CGRect) {    
        let circlePath = UIBezierPath(arcCenter: CGPoint(x: rect.midX, y: rect.midY), radius: 20, startAngle: 0, endAngle:(.pi * 2), clockwise: true)
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = Constants.defaultCircleFillColor
        shapeLayer.strokeColor = myColor.cgColor
        shapeLayer.lineWidth = 1.5
        self.layer.addSublayer(shapeLayer)
    }

    func setColor(_ color: UIColor) {
        myColor = color
        // shapeLayer.strokeColor = color.cgColor
    }
}

Upvotes: 1

David Pasztor
David Pasztor

Reputation: 54775

As you have stated in comments, by setting up breakpoints, you realised that setColor is called before draw would be called. This is expected behaviour due to the drawing cycle of UIView.

As the UIView documentation states in the The View Drawing Cycle part,

View drawing occurs on an as-needed basis. When a view is first shown, or when all or part of it becomes visible due to layout changes, the system asks the view to draw its contents. For views that contain custom content using UIKit or Core Graphics, the system calls the view’s draw(_:) method.

This means that in cellForRowAt the draw method is not yet called, since the cell itself is not even returned and not displayed on the screen. You could fix this issue, by setting up a new instance variable for your CircleView class, that stores the colour you want to use in setColor and use this colour directly in the draw method. In cellForRowAt just assign the colour you used as an input to setColor before to this new instance property and the issue should be fixed.

Upvotes: 1

Related Questions