Search code examples
iosswiftoption-typeoptional-binding

Why value of type 'UIView' has no member from reference UIView variable?


I want to reference a variable to a specific function. However, there is an error called Value of type 'UIView' has no member 'lineTo' Clearly, the whatSelectObject variable contains the classes in which a member exists. So I used If statement, "optional binding." But the results are the same.

//: A UIKit based Playground for presenting user interface

import UIKit
import PlaygroundSupport

class ObjectView: UIView {
    var outGoingLine : CAShapeLayer?
    var inComingLine : CAShapeLayer?

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func lineTo(connectToObj: ObjectView) -> CAShapeLayer {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: self.frame.maxX, y: self.frame.midY))
        path.addLine(to: CGPoint(x: connectToObj.frame.minX, y: connectToObj.frame.midY))

        let line = CAShapeLayer()
        line.path = path.cgPath
        line.lineWidth = 5
        line.fillColor = UIColor.clear.cgColor
        line.strokeColor = UIColor.gray.cgColor
        connectToObj.inComingLine = line
        outGoingLine = line
        return line
    }
}


class MyViewController : UIViewController {
    var whatSelectView: UIView?
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let object = ObjectView(frame: CGRect(x: 10, y: 100, width: 100, height: 100))
        object.backgroundColor = UIColor.orange
        view.addSubview(object)

        whatSelectView = object

        let object2 = ObjectView(frame: CGRect(x: 200, y: 200, width: 100, height: 100))
        object2.backgroundColor = UIColor.red
        view.addSubview(object2)

        if let s = whatSelectView {
            view.layer.addSublayer(s.lineTo(connectToObj: object2)) // error
        }

        self.view = view
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

!This code is an example of a reduced code in question to help the respondent understand it. I can refer directly to the object variable, but I must refer to whatSelectView without fail.

Why does cause an error in the reference variable? Or was I wrong with the Optional binding?


Solution

  • Indeed UIView does not have any member nor method called lineTo, and you have to use casting (as? in your case)

    like this:

    if let s = whatSelectView as? ObjectView {
         view.layer.addSublayer(s.lineTo(connectToObj: object2)) // error
    }
    

    Beside that, the reference to whatSelectView should be weak because view already keeps a strong reference to it since it object is a subview.

    And you don't need the if condition at all, you already have reference to the view using object.

    So, Here is my proposal for better implementation

    class MyViewController : UIViewController {
        weak var whatSelectView: ObjectView?
    
        override func loadView() {
            let view = UIView()
            view.backgroundColor = .white
    
            let object = ObjectView(frame: CGRect(x: 10, y: 100, width: 100, height: 100))
            object.backgroundColor = UIColor.orange
            view.addSubview(object)
    
            whatSelectView = object
    
            let object2 = ObjectView(frame: CGRect(x: 200, y: 200, width: 100, height: 100))
            object2.backgroundColor = UIColor.red
            view.addSubview(object2)
    
            let shape = object.lineTo(connectToObj: object2)
            view.layer.addSublayer(shape)
    
            self.view = view
        }
    }