1

I am trying to create the bellow property wrapper

@propertyWrapper
public struct ConstraintValue {

    private let view:UIView
    private let contraint:NSLayoutConstraint

    public init(contraint:NSLayoutConstraint,view:UIView) {
        self.contraint = contraint
        self.view = view
    }

    public var wrappedValue: CGFloat {
        set {
            contraint.constant = newValue
            view.layoutIfNeeded()

        }

        get {
            return contraint.constant
        }

    }
}

But when I try to use it I am getting "Cannot use instance member within property initializer; property initialisers run before 'self' is available"

@IBOutlet weak private var imageViewHeightConstraint: NSLayoutConstraint!
 @ConstraintValue(contraint: imageViewHeightConstraint, view: self)
 public var imageHeight:CGFloat

Is there a way to passthrough this error ?

1 Answers1

0

You can't use self and imageViewHeightConstraint to initialise this property, because it does not exist at this moment.

Easiest solution would be to pass constraint and view to wrapper in view's init() method, like this:

@propertyWrapper
public struct ConstraintValue {

    var view: UIView!
    var constraint: NSLayoutConstraint!

    public var wrappedValue: CGFloat {
        set {
            constraint.constant = newValue
            view.layoutIfNeeded()

        }

        get {
            return constraint.constant
        }
    }
}

class OurView: UIView {

    var imageViewHeightConstraint: NSLayoutConstraint!

    @ConstraintValue public var imageHeight:CGFloat

    init() {
        super.init(size: .zero)
        _imageHeight.view = self
        _imageHeight.constraint = imageViewHeightConstraint
    }
}

Alternatively you can use KeyPath for imageViewHeightConstraint and pass self to property wrapper in view's init(). Something like this:

protocol DynamicImageView: UIView {
    var imageViewHeightConstraint: NSLayoutConstraint! { get set }
}

@propertyWrapper
public struct ConstraintValue {

    var view: DynamicImageView?
    private let constraintKeyPath: WritableKeyPath<DynamicImageView, NSLayoutConstraint>

    init(constraintKeyPath: WritableKeyPath<DynamicImageView, NSLayoutConstraint>) {
        self.constraintKeyPath = constraintKeyPath
    }

    public var wrappedValue: CGFloat {
        set {
            view?[keyPath: constraintKeyPath].constant = newValue
            view?.layoutIfNeeded()
        }

        get {
            view?[keyPath: constraintKeyPath].constant ?? 0.0
        }
    }
}

class OurView: UIView, DynamicImageView{

    var imageViewHeightConstraint: NSLayoutConstraint!

    @ConstraintValue(constraintKeyPath: \.imageViewHeightConstraint)
    public var imageHeight:CGFloat

    init() {
        super.init(frame: .zero)
        _imageHeight.view = self
    }
}
Lieksu
  • 71
  • 4
  • Thanks for the response. Is there a way to access view without passing it like this ? – Christos Chadjikyriacou Mar 06 '20 at 12:46
  • @ChristosChadjikyriacou you can have this property in some kind of container view and make it change the constraint in one of its child views, but ultimately it's not very good case for property wrapper. Using property observer (didSet) would be easier, because you don't have to initialise a struct for it to work. – Lieksu Mar 07 '20 at 13:30