John Xavier
John Xavier

Reputation: 73

setNeedsDisplay() is not calling draw(_ rect: CGRect)

I found similar questions in this website but none of them solved my issue. Please carefully read the whole code. The setNeedsDisplay() function is not calling the draw(_ rect: CGRect) when I want to draw a line on MyView.

I created a view called "myView" in storyboard as sub View of "MyViewController" and created a IBOutlet to the view controller.

Then I created a class called "MyViewClass" and set it as the class of "myView" in storyboard.

I set the bool value drawLine to true and call function updateLine(), the problem is the setNeedsDispaly() is not triggering the draw(_ rect: CGRect) function.

import UIKit

class MyViewClass : UIView{

    var drawLine = Bool() // decides whether to draw the line
    func updateLine(){
        setNeedsDisplay()
    }
    override func draw(_ rect: CGRect) {
        if drawLine == true{
            guard let context = UIGraphicsGetCurrentContext() else{
                return
            }
            context.setLineWidth(4.0)
            context.setStrokeColor(UIColor.red.cgColor)
            context.move(to: CGPoint(x: 415 , y: 650))
            context.addLine(to: CGPoint(x:415 , y: 550))
            context.strokePath()
        }

    }
}
class myViewController:UIViewController {
    @IBOutlet weak var insideView: UIView!
    override func viewDidLoad() {
        super.viewDidLoad()


        // Do any additional setup after loading the view.
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let myViewInstance = MyViewClass()
        myViewInstance.drawLine = true
        myViewInstance.updateLine()
    }
}

I'm fairly new to Swift. Any help will appreciated. Thanks.

Upvotes: 1

Views: 1760

Answers (2)

vacawama
vacawama

Reputation: 154691

You have 2 issues:

  1. you're not giving the new view a frame so it defaults to zero width and zero height
  2. you're not adding the view to the view heirarchy

    let myViewInstance = MyViewClass(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height))
    myViewInstance.backgroundColor = .white  // you might want this a well
    myViewInstance.drawLine = true
    self.view.addSubview(myViewInstance)
    myViewInstance.updateLine()
    

A cleaner way to have your view redraw when a property (such as drawLine) is changed is to use a property observer:

var drawLine: Bool = true {
    didSet {
        setNeedsDisplay()
    }
}

That way, the view will automatically redraw when you change a property and then you don't need updateLine().

Note that setNeedsDisplay just schedules the view for redrawing. If you set this on every property, the view will only redraw once even if multiple properties are changed.


Note: viewDidLoad() would be a more appropriate place to add your line because it is only called once when the view is created. viewWillAppear() is called anytime the view appears so it can be called multiple times (for instance in a UITabView, viewWillAppear() is called every time the user switches to that tab).

Upvotes: 2

chirag90
chirag90

Reputation: 2240

MyView class

class myView : UIView {
    var updateView : Bool = false {
        didSet {
            setNeedsDisplay()
        }
    }

    override func draw(_ rect: CGRect) {
        print("in Draw")

    }

}

MyViewController

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let view = myView()
        view.updateView = true
    }
}

In the storyboard i had setup the view class as myView therefore its visible.

StoryBoard Image

Output Image

Hopefully this will help you.

Upvotes: 0

Related Questions