简体   繁体   中英

Programmatically assigning class to a UIView created in Storyboard (Swift)

Question: How do I programmatically assign a class to a UIView created in Storyboard?

I have a UIView created in IB ( graphView ) in which I would like to display one of several graphs, their definitions created in their respective classes.

To do this, I created another view programmatically then assigned the class of the selected graph to that view, then added it to the original view:

@IBOutlet weak var graphView: UIView!

func displayGraph(choice: Int) {
    var selectedGraphView = UIView()        
    switch choice {
        case 1: selectedGraphView = class1()
        case 2: selectedGraphView = class2()
        case 3: selectedGraphView = class3()
        default: selectedGraphView = class1()
    }
    selectedGraphView.frame = CGRect(x: 0, y: 0, width: graphView, height: graphView)
    graphView.addSubview(selectedGraphView)
}

Things seem to work, but do I really need that second UIView ( selectedGraphView )?

I tried changing it to:

func displayGraph2(choice: Int) {
    switch choice {
        case 1: graphView = class1()
        case 2: graphView = class2()
        case 3: graphView = class3()
        default: graphView = class1()
    }
}

but I don't know how to get the contents of the class to run.

Can I do it this way? If so, what is missing?

EDIT: Actually, there are 3 UIViews: the one in IB, the one to hold the graph, and the class itself creates one.

If, say class1 has nothing but print("entered class1") , in the first case ( displayGraph ), I see "entered class1" printed. In the second ( displayGraph2 ), I get nothing.

EDIT2: I can assign any of the classes to graphView in IB and get the print message from the class. How do I assign a class to graphView programmatically (since what I attempted in displayGraph2 doesn't work)?

When you add a UIView in Storyboard, you can assign its Custom Class. However, at run-time, you cannot change its class this way:

@IBOutlet var graphViewHolder: UIView!

override func viewDidLoad() {
    super.viewDidLoad()

    // cannot do this
    let v = View2Class()
    graphViewHolder = v

}

One common approach, as you've done, is to add a UIView in your Storyboard as a "holder" (or "container") view. Then, in code, instantiate an instance of your desired Custom Class and add it as a subview to the "holder" view.

However, if you do this:

selectedGraphView.frame = CGRect(x: 0, y: 0, width: graphView, height: graphView)
graphView.addSubview(selectedGraphView)

The newly added subview will have the size of the "holder" view, and you'll run into layout issues if / when the holder view changes size (such as on device rotation).

So, you can do it like this:

// let's name it "graphViewHolder" so we know we're using it as a "holder" view
@IBOutlet var graphViewHolder: UIView!

var currentChoice: Int = 1

override func viewDidLoad() {
    super.viewDidLoad()

    displayGraph(choice: 1)
}

func displayGraph(choice: Int) {
    var selectedGraphView: UIView?
    switch choice {
    case 1: selectedGraphView = View1Class()
    case 2: selectedGraphView = View2Class()
    case 3: selectedGraphView = View3Class()
    default: selectedGraphView = View1Class()
    }
    // unwrap optional
    guard let sgv = selectedGraphView else { return }
    // does the "holder" view already have any subviews?
    //  if so, remove them
    graphViewHolder.subviews.forEach { v in
        v.removeFromSuperview()
    }
    // add the new view as a subview of the "holder" view
    graphViewHolder.addSubview(sgv)
    // we need to give the new view auto-layout properties
    sgv.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        // constrain it to all 4 sides of the "holder" view
        sgv.topAnchor.constraint(equalTo: graphViewHolder.topAnchor),
        sgv.leadingAnchor.constraint(equalTo: graphViewHolder.leadingAnchor),
        sgv.trailingAnchor.constraint(equalTo: graphViewHolder.trailingAnchor),
        sgv.bottomAnchor.constraint(equalTo: graphViewHolder.bottomAnchor),
    ])
}

Now the subview will follow the "holder" view's constraints.

Note that if you want to do something to the loaded view, you'll have to make sure it's the right class.

So, for example, if we want to call a custom func in View2Class :

    // make sure the "holder" view has a subview
    guard let v = graphViewHolder.subviews.first else {
        print("Graph View Holder has no subviews!")
        return
    }
    // make sure the subview is a View2Class instance
    guard let v2 = v as? View2Class else {
        print("The Holder subview is not View2Class!")
        return
    }
    // call a func in View2Class
    v2.myFunc()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM