25

I have written a custom UIView and I found a strange problem. I think this is related to a very fundamental concept but I just do not understand it, sigh.....

class ArrowView: UIView {

    override func draw(_ rect: CGRect) {

        let arrowPath = UIBezierPath.bezierPathWithArrowFromPoint(startPoint: CGPoint(x:bounds.size.width/2,y:bounds.size.height/3), endPoint: CGPoint(x:bounds.size.width/2, y:bounds.size.height/3*2), tailWidth: 8, headWidth: 24, headLength: 18)

        let fillColor = UIColor(red: 0.00, green: 0.59, blue: 1.0, alpha: 1.0)
        fillColor.setFill()
        arrowPath.fill()
    }
}

this code works fine but if I have grabbed this line out of the override draw function it does not compile. The error says I can not use the bounds property.

let arrowPath = UIBezierPath.bezierPathWithArrowFromPoint(startPoint: CGPoint(x:bounds.size.width/2,y:bounds.size.height/3), endPoint: CGPoint(x:bounds.size.width/2, y:bounds.size.height/3*2), tailWidth: 8, headWidth: 24, headLength: 18)

Cannot use instance member 'bounds' within property initializer; property initializers run before 'self' is available

I don not understand why I cannot use this bounds out of the func draw

Ryan Poolos
  • 18,421
  • 4
  • 65
  • 98
Billy
  • 701
  • 1
  • 8
  • 26
  • Possible duplicate of [Error: Cannot use instance member within property initializer - Swift 3](https://stackoverflow.com/questions/40710909/error-cannot-use-instance-member-within-property-initializer-swift-3) – Filburt Jul 31 '17 at 18:59
  • 1
    you can fix this issue by using computed property [Example of Computed Property](https://stackoverflow.com/a/39986404/6689101) – zombie Jul 31 '17 at 20:19

1 Answers1

50

So if we decode the error message you can figure out whats wrong. It says property initializers run before self is available so we need to adjust what we're doing since our property depends on bounds which belongs to self. Lets try a lazy variable. You can't use bounds in a let because it doesn't exist when that property is created because it belongs to self. So at init self isn't complete yet. But if you use a lazy var, then self and its property bounds will be ready by the time you need it.

lazy var arrowPath = UIBezierPath.bezierPathWithArrowFromPoint(startPoint: CGPoint(x: self.bounds.size.width/2,y: self.bounds.size.height/3), endPoint: CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/3*2), tailWidth: 8, headWidth: 24, headLength: 18)
Ryan Poolos
  • 18,421
  • 4
  • 65
  • 98
  • how it affect on behaviour of arrowPath? I mean whats going on to the project after changing let to lazey var? – neda Derakhshesh Jul 28 '19 at 13:15
  • 3
    lazy var means that this property is null until its accessed for the first time. Critically here that means that `self.bounds` is available because this property cannot be accessed until after `self` is initialized. – Ryan Poolos Jul 29 '19 at 19:23