bflora2
bflora2

Reputation: 753

Dynamically reference an IBOutlet in Swift?

I have a view controller with 100 IBOutlets that I want to hide or display depending on whether the name of that IBOutlet is found as a key in Dictionary.

How on earth do I do this? My intuition points me to

for item in Dict {
    // try to refer to an IBOutlet using data from my Dictionary 
    self.\(item.key).hidden = false
}

Swift obviously throws up and error when I try to do this.

I'm new to this and not even sure what to call what I'm trying to do. Is it even possible to do this? Is there a much better way to do this?

I'd like to avoid writing 100 "if" statements.

Upvotes: 3

Views: 1055

Answers (2)

user7014451
user7014451

Reputation:

I agree with Sulthan that this is a case were constraints and IB are poor design. I coded this up as a template the should help you out. Notes:

  • The global width and height variables are there to make it easy to change. Poor coding practice but there for testing purposes.
  • I split out the creation of the subviews from the backgroundColor choice to give you an idea on how to iterate through the subviews quickly.
  • The "seedValue" piece is probably not needed for you. I needed it to differentiate between rows.
  • Most importantly, since you only state you have 100 "UIViews", I would recommend subclassing UIView as something. Not only can you create a convenience initializer (making is easy to init additional properties and common subviews), you can iterate through the superview's subviews as your subclass type, allowing you to use the tag property elsewhere.

The code:

let width:CGFloat = 20
let height:CGFloat = 20

override func viewDidLoad() {
    super.viewDidLoad()
    create100subviews()
    for view in view.subviews as [UIView] {
        color(view)
    }
}

func create100subviews() {
    for i in 1...100 {
        let newView = UIView(frame: calculateFrame(width: width, height: height, tag: i))
        newView.tag = i
        view.addSubview(newView)
    }
}

func calculateFrame(width:CGFloat, height:CGFloat, tag:Int) -> CGRect {
    let (row,column) = calculateRowAndColumn(tag: tag)
    return CGRect(x: ((CGFloat(column)*width)+20), y: ((CGFloat(row)*height)+40), width: width, height: height)
}

func calculateRowAndColumn(tag:Int) -> (Int,Int) {
    // the returned tuple (row,column) is zero-based
    var row:Int = 0
    var column = tag
    while column > 10 {
        row += 1
        column -= 10
    }
    column -= 1
    return (row,column)
}

func color(_ view: UIView) {
    let (row,column) = calculateRowAndColumn(tag: view.tag)
    var seedValue = column + row + 1
    while seedValue > 10 {
        seedValue -= 10
    }
    switch seedValue {
    case 1:
        view.backgroundColor = UIColor.black
    case 2:
        view.backgroundColor = UIColor.red
    case 3:
        view.backgroundColor = UIColor.green
    case 4:
        view.backgroundColor = UIColor.blue
    case 5:
        view.backgroundColor = UIColor.black
    case 6:
        view.backgroundColor = UIColor.red
    case 7:
        view.backgroundColor = UIColor.green
    case 8:
        view.backgroundColor = UIColor.blue
    case 9:
        view.backgroundColor = UIColor.black
    case 10:
        view.backgroundColor = UIColor.red
    default:
        break
    }
}

Upvotes: 0

Sweeper
Sweeper

Reputation: 273898

There should be lots of other, better ways to do this.

  1. Have you considered adding all the outlets to dictionary?

    var outletDict: [String: UIView] = ["outlet1": outlet1, "outlet2": outlet2,
        "outlet3", outlet3 ... "outlet100": outlet100]
    

Then you can just access the outlets using this dictionary:

for item in Dict {
    outletDict[item.key].hidden = false
}
  1. Have you tried not using outlets and dynamically create the views instead? I mean, it's really rare to have 100 outlets. I wonder how you connected them all. They should be in a pattern or something. If that's true, you can create your views with for loops and add them to the dictionary mentioned above.

  2. If none of the above applies to you, the only solution I can think of is this:

    for item in Dict {
        let mirror = Mirror(reflecting: self)
        let outlet = mirror.children.filter { $0.label == item.key }.first?.value as? UIView
        outlet?.hidden = false
    }
    

Upvotes: 2

Related Questions