1

I am playing a bit with @IBInspectables. I have created a reusable custom View which has some @IBInspectables.

Is there no way to give priority to the @IBInspectables to get executed?

In the following case to modify the color or the font of the placeholder Its needed to do through an attributed text. So I need that some @IBInspectables like Font, Color, get executed before the @IBInspectable which sets the placeholder text.

In this case I have done the workaround to get always the placeholder Color. However, I want to add more attributes to the placeholder like the Font but if I don't know which order are they going to get executed I would have to set the "attributedPlaceholder" from every IBInspectable that modifies the placeholder)

@IBInspectable
var placeholder: String? {
    didSet {
        guard let placeholder = placeholder else { return }

        textField.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [NSAttributedStringKey.foregroundColor: placeholderColor ?? UIColor.red])
    }
}

@IBInspectable
var placeholderColor: UIColor? {
    didSet {
        guard let placeholderColor = placeholderColor else { return }

        textField.attributedPlaceholder = NSAttributedString(string: textField.placeholder != nil ? textField.placeholder! : "", attributes: [NSAttributedStringKey.foregroundColor: placeholderColor])
    }
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Pablo Sanchez Gomez
  • 1,438
  • 16
  • 28
  • I don't think so, but maybe if you were more specific about the *actual* issue that may make a difference. `@IBInspectable` is analogous to exposing a "property" to Interface Builder - something that back in the day would be like setting a property to a "control" in Visual Basic. That real question for you is **why**? Why would one care about having a priority to a `String` over a `UIColor` in design mode? Without that sort of clarity, I'm guessing I'm either not understanding the actual issue or the answer is "no". –  Mar 20 '18 at 19:19
  • To set the color of the Textfield placeholder I need to do it through an attributed String because they do not allow you to modify the directly the color of the placeholder. So in the example that I have written I have to do the workaround because if I just write textField.placeholder = placeholder, it won't set the color if the placeholder color gets executed before. Also, I want to add a custom font to the placeholder through an IBInspectable and I have to write in one way or in another depending which gets executed before. – Pablo Sanchez Gomez Mar 20 '18 at 19:29
  • Ok. First, I cannot remove my down vote without an edit. I would - and will - but maybe you should add a bit of this detail to your question. (And I apologize for this.) Second, I don't think this will help you out - I still don't think you can "prioritize" separate properties for a design-time tool - but check out if the functionality in `prepareForInterfaceBuilder()`, an override in `UIView`, can accomplish something close to what you are trying to do. –  Mar 20 '18 at 19:34
  • No worries, after seeing your reply, I was already editing the question because I explained myself in a really bad way :(. Maybe I will check if in some point of the UIView life are all of them loaded and I will set the attributed string there. (To check the functionality I can also do compiling the app but it creates an unexpected behaviors because if they are executed in different threads it is a race) Before posting the question I did some tests and in some views the placeHolder is executed before than the color and viceversa. – Pablo Sanchez Gomez Mar 20 '18 at 19:50
  • @Sulthan has a very good answer. One last point - *"...if they are executed in different threads..."* - I may be misunderstanding things, but if you are speaking about a UI change, they *all* belong in the main thread. –  Mar 20 '18 at 20:28
  • Yeah it is true, but as 2 instances of the same class with the same keys & values get executed in different orders, it got me think that maybe there is something asynchronous in the call of the @IBInspectable or at the time of detecting them or smth – Pablo Sanchez Gomez Mar 23 '18 at 07:35

1 Answers1

4

You should write the setters in a way that the order of calls won't matter. This is not only about the order of calls in Interface Builder, this is also about the order when called programatically.

It shouldn't matter whether you call:

view.placeholder = 
view.placeholderColor = 

or

view.placeholderColor = 
view.placeholder = 

A sample implementation:

@IBInspectable
var placeholder: String? {
   didSet {
      updatePlaceholder()
   }
}

@IBInspectable
var placeholderColor: UIColor? {
   didSet {
      updatePlaceholder()
   }
}

private func updatePlaceholder() {
   textField.attributedPlaceholder = NSAttributedString(
       string: placeholder ?? "",
       attributes: [.foregroundColor: placeholderColor ?? UIColor.red]
   )
}
Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • Thank you, I don't know how I didn't think in this way! :). I also added guard let placeholder = placeholder, let placeholderColor = placeholderColor else { return }, to avoid that textField.attributedPlaceholder gets executed more than once, and also I want compulsory values in the @IBInspectables :) – Pablo Sanchez Gomez Mar 23 '18 at 07:28