0

I am trying to create a customView using xib below is the code

import UIKit

class CustomView: UIView {

@IBOutlet var CustomView: UIView!

private var _isSelecteda:Bool!
var isSelecteda: Bool {
    get {
        return _isSelecteda
    }
    set {
        _isSelecteda = isSelecteda
        if _isSelecteda {
            CustomView.backgroundColor = UIColor.white
            CustomView.layer.borderColor = UIColor.black.cgColor
        }
        else {
            CustomView.backgroundColor = Colors.searchGameCellBackgroundColor
            CustomView.layer.borderColor = Colors.priceLabelBorderColor?.cgColor
        }
    }
}



override init(frame: CGRect) {

    super.init(frame: frame) 
    commonInit()

}

required init?(coder aDecoder: NSCoder) {

    super.init(coder: aDecoder)
    commonInit()

}


private func commonInit() {
    Bundle.main.loadNibNamed("CustomView", owner: self, options: nil)
    addSubview(CustomView)
    self._isSelecteda = false
    CustomView.layer.cornerRadius = 3
    CustomView.layer.borderWidth = 1
    self.clipsToBounds = true
    CustomView.frame = self.bounds

}

@IBAction func btnSelectedTapped(_ sender: Any) {
    isSelecteda = !isSelecteda
}
}

When ever I try to access isSelecteda the private declaration of _isSelecteda is called and resets the value . My objective is to set the value of isSelected from a ViewController and change its background color.

According to my understanding that should not be the case. Its very strange

Note : I am using Xcode 9.4.1 with Swift 4.1

vinbhai4u
  • 1,329
  • 3
  • 19
  • 36

2 Answers2

1

Why not use didSet for that?

didSet {
    if isSelecteda {
        CustomView.backgroundColor = UIColor.white
        CustomView.layer.borderColor = UIColor.black.cgColor
    } else {
        CustomView.backgroundColor = Colors.searchGameCellBackgroundColor
        CustomView.layer.borderColor = Colors.priceLabelBorderColor?.cgColor
    }
}

The reason for your value being reset is probably because your variable still has the oldValue which you are using for comparison inside the setter. When you call the variable inside the setter, the getter gets the oldValue because the newValue has not yet been set.


Note: It's preferable to follow the naming conventions as per the official naming guidelines. Variables are lower camel case. CustomView -> customView.

Rakesha Shastri
  • 11,053
  • 3
  • 37
  • 50
1

As per my understanding, you should change the setter like this:

set {
    _isSelecteda = newValue
    if _isSelecteda {
        CustomView.backgroundColor = UIColor.white
        CustomView.layer.borderColor = UIColor.black.cgColor
    }
    else {
        CustomView.backgroundColor = Colors.searchGameCellBackgroundColor
        CustomView.layer.borderColor = Colors.priceLabelBorderColor?.cgColor
    }
}

The newValue variable is the actual value that is being received when the setter is called.

When you would do this:

customView.isSelecteda = false

The setter gets 'false' in newValue variable. You set this value to your private variable and perform the subsequent functions based on this value.

You can find more about 'oldValue' and 'newValue' in this question: Click Here

EDIT: A justification as to how this is the correct behaviour:

get {
    return _isSelecteda                      // false - from init call
}
set {
    _isSelecteda = isSelecteda               // isSelecteda getter called from above returning false, newValue is neglected
    if _isSelecteda {                        // returns false
        CustomView.backgroundColor = UIColor.white
        CustomView.layer.borderColor = UIColor.black.cgColor
    }
    else {
        CustomView.backgroundColor = Colors.searchGameCellBackgroundColor
        CustomView.layer.borderColor = Colors.priceLabelBorderColor?.cgColor
    }
}
Rishabh
  • 465
  • 5
  • 14
  • Okay this works but my question still remains. why is _isSelecteda is re-initialised when we call isSelected. That's quite an unexpected behaviour. – vinbhai4u Sep 05 '18 at 06:39
  • It's actually the correct behaviour. It is not reinitialised. When you assign a variable in its own setter, it will always get the default value because the new value is never assigned to it. When the setter is setting its own value, the private declaration will be called from getter, which is always going to be false as its assigned in the init method. Let me add a better explanation to the answer. – Rishabh Sep 05 '18 at 06:53
  • @vinbhai4u Using private variables not the _Swifty_ to go about achieving what you want. This defeats the whole purpose of a getter and setter. – Rakesha Shastri Sep 05 '18 at 06:57
  • @RakeshaShastri why is that so? I don't want to expose my UI components of customView to the ViewController. – vinbhai4u Sep 05 '18 at 07:02
  • @Rishabh I might need to get more understanding on that, but I see your point. will do some research on it. Thanks for the help mate – vinbhai4u Sep 05 '18 at 07:03
  • @vinbhai4u i am talking about you using this `private var _isSelecteda:Bool!`. – Rakesha Shastri Sep 05 '18 at 07:03
  • @RakeshaShastri So what would be the alternative. declare _isSelected as public? – vinbhai4u Sep 05 '18 at 07:07
  • @vinbhai4u ok maybe i have the wrong understanding here. I don't think i understood what you meant by exposing UI component. Why do you use the placeholder variable? What is achieved by using that? – Rakesha Shastri Sep 05 '18 at 07:10
  • @RakeshaShastri I have a UIButton inside the CustomView which should trigger a state of change for the CustomView. Also I need to know and change the state from an another ViewController. thats y the placeholder variable. Does that makes sense? – vinbhai4u Sep 05 '18 at 07:20
  • 1
    @vinbhai4u But you aren't handling the state change from another view controller inside this one or using that private variable for that. From what i can see, it is redundantly holding the value of your variable which you can achieve without using an extra variable by using `didSet`. You could even just use the `newValue` in the `if` statement imo. – Rakesha Shastri Sep 05 '18 at 07:23
  • @RakeshaShastri Ohh now I get you point. Actually I don't need the arbitrary variable to keep the state. U can actually get it from isSelected itself. I always thought set and get is required maybe becoz I am coming from a c# background. So another question pops up can we disable get from a variable? – vinbhai4u Sep 05 '18 at 07:32
  • 1
    what @RakeshaShastri is trying to say is that, you should just have one variable and use its setter getter as you are, no need for additional variable just to hold the same value. – Rishabh Sep 05 '18 at 07:33
  • @Rishabh your answer is also valid. but after discussion with Rakesha it seems his answer is more valid. So I am accepting his answer. Always thanks for your help – vinbhai4u Sep 05 '18 at 07:34
  • @vinbhai4u i don't think you can do that. Because the whole point of a variable would be get it's value somewhere. But if you do not specify set, you can disable setter for a variable. You can easily verify it in Playgrounds. – Rakesha Shastri Sep 05 '18 at 07:37
  • Yep got it guys :). Everything seems better now. Really long conversation for a simple thing hehehe – vinbhai4u Sep 05 '18 at 07:47
  • 1
    @vinbhai4u ithokke enth :P – Rakesha Shastri Sep 05 '18 at 09:11
  • 1
    @RakeshaShastri malayali anu alle :P – vinbhai4u Sep 05 '18 at 09:16