Home > Software engineering >  I want to set label & Imageview in centre of myView but App crashing after adding some constraints
I want to set label & Imageview in centre of myView but App crashing after adding some constraints

Time:03-25

Error:-

Thread 1: "Unable to activate constraint with anchors <NSLayoutXAxisAnchor:0x280af8500 \"UILabel:0x103dc4fa0.centerX\"> and <NSLayoutXAxisAnchor:0x280af89c0 \"UIView:0x103dc49d0.centerX\"> because they have no common ancestor.  Does the constraint or its anchors reference items in different view hierarchies?  That's illegal."

I've created a function programmatical in Base view controller which returns a view & I've added some constraints to its

Function:-

func getMarker (lbl:String, img:UIImage) -> UIView {
        let myView = UIView(frame: CGRect.zero)
        myView.center = CGPoint(x: 50, y: 160)
        myView.backgroundColor = UIColor.clear


        let imageView = UIImageView(image: img)
        imageView.frame = CGRect(x: 0, y: 0, width: 20, height: 40)
        myView.addSubview(imageView)


        let label = UILabel(frame: CGRect(x: 0, y: 45, width: 120, height: 30))
        label.text = lbl
        label.textAlignment = .center
        label.adjustsFontSizeToFitWidth = true
        label.textColor = UIColor.black
        label.backgroundColor = UIColor.white
        label.layer.borderColor = UIColor.lightGray.cgColor
        label.layer.borderWidth = 0.5
        label.layer.cornerRadius = 5
        label.layer.masksToBounds = true
        label.sizeToFit()

        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: myView.centerXAnchor),
            imageView.centerXAnchor.constraint(equalTo: myView.centerXAnchor)
        ])

        myView.addSubview(label)

        return myView
    }

calling function in another controller but it crashing and showing me the error which I mentioned above

Calling function:-

getMarker(lbl: device.name ?? "", img: (UIImage(named: icfile) ?? UIImage(named: "truck_1_orange")!))

CodePudding user response:

U need to add subview first, then activate layout. Label is not in myView subviews in your code. It is not in any hierarchy at the moment of layout constraint activation.

myView.addSubview(label)    
NSLayoutConstraint.activate([
        label.centerXAnchor.constraint(equalTo: myView.centerXAnchor),
        imageView.centerXAnchor.constraint(equalTo: myView.centerXAnchor)
    ])
    

CodePudding user response:

You need to configure the layout of your subviews (imageView and label) in order to have them sized and positioned where you want them.

Take a look at this example code:

class MarkerVC: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBlue
        
        guard let img = UIImage(named: "img80x80")
        else {
            fatalError("Could not load sample image!!!")
        }

        let markerView1 = getMarker(lbl: "Testing", img: img)
        
        let markerView2 = getMarkerAutoSized(lbl: "Testing", img: img)

        markerView1.translatesAutoresizingMaskIntoConstraints = false
        markerView2.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(markerView1)
        view.addSubview(markerView2)

        // respect safe area
        let safeG = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // we have to set both Position and Size constraints
            //  for markerView1
            markerView1.topAnchor.constraint(equalTo: safeG.topAnchor, constant: 20.0),
            markerView1.widthAnchor.constraint(equalToConstant: 240.0),
            markerView1.heightAnchor.constraint(equalToConstant: 100.0),
            markerView1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            
            // markerView2 uses its subviews to set its own size
            //  so we only need position constraints
            markerView2.topAnchor.constraint(equalTo: markerView1.bottomAnchor, constant: 20.0),
            markerView2.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            
        ])

        // so we can see the view frames
        markerView1.backgroundColor = .systemRed
        markerView2.backgroundColor = .systemRed

    }
    
    func getMarker (lbl:String, img:UIImage) -> UIView {
        
        let myView = UIView()
        myView.backgroundColor = UIColor.clear
        
        let imageView = UIImageView(image: img)

        let label = UILabel()
        label.text = lbl
        label.textAlignment = .center
        label.adjustsFontSizeToFitWidth = true
        label.textColor = UIColor.black
        label.backgroundColor = UIColor.white
        label.layer.borderColor = UIColor.lightGray.cgColor
        label.layer.borderWidth = 0.5
        label.layer.cornerRadius = 5
        label.layer.masksToBounds = true

        imageView.translatesAutoresizingMaskIntoConstraints = false
        label.translatesAutoresizingMaskIntoConstraints = false
        
        myView.addSubview(imageView)
        myView.addSubview(label)

        NSLayoutConstraint.activate([

            // image view Width and Height
            imageView.widthAnchor.constraint(equalToConstant: 20.0),
            imageView.heightAnchor.constraint(equalToConstant: 40.0),

            // labe Width and Height
            label.widthAnchor.constraint(equalToConstant: 120.0),
            label.heightAnchor.constraint(equalToConstant: 30.0),
            
            // image view aligned to Top
            imageView.topAnchor.constraint(equalTo: myView.topAnchor),
            // centered Horizontally
            imageView.centerXAnchor.constraint(equalTo: myView.centerXAnchor),
            
            // label 5-pts below image view
            label.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 5.0),
            // centered Horizontally
            label.centerXAnchor.constraint(equalTo: myView.centerXAnchor),
            
        ])
        
        return myView
    }
    
    func getMarkerAutoSized (lbl:String, img:UIImage) -> UIView {
        
        let myView = UIView()
        myView.backgroundColor = UIColor.clear
        
        let imageView = UIImageView(image: img)
        
        let label = UILabel()
        label.text = lbl
        label.textAlignment = .center
        label.adjustsFontSizeToFitWidth = true
        label.textColor = UIColor.black
        label.backgroundColor = UIColor.white
        label.layer.borderColor = UIColor.lightGray.cgColor
        label.layer.borderWidth = 0.5
        label.layer.cornerRadius = 5
        label.layer.masksToBounds = true
        
        imageView.translatesAutoresizingMaskIntoConstraints = false
        label.translatesAutoresizingMaskIntoConstraints = false
        
        myView.addSubview(imageView)
        myView.addSubview(label)
        
        NSLayoutConstraint.activate([
            
            // image view Width and Height
            imageView.widthAnchor.constraint(equalToConstant: 20.0),
            imageView.heightAnchor.constraint(equalToConstant: 40.0),
            
            // labe Width and Height
            label.widthAnchor.constraint(equalToConstant: 120.0),
            label.heightAnchor.constraint(equalToConstant: 30.0),
            
            // image view aligned to Top
            imageView.topAnchor.constraint(equalTo: myView.topAnchor),
            // centered Horizontally
            imageView.centerXAnchor.constraint(equalTo: myView.centerXAnchor),
            
            // label 5-pts below image view
            label.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 5.0),
            // centered Horizontally
            label.centerXAnchor.constraint(equalTo: myView.centerXAnchor),
            
            // to auto-size myView
            label.leadingAnchor.constraint(equalTo: myView.leadingAnchor),
            label.trailingAnchor.constraint(equalTo: myView.trailingAnchor),
            label.bottomAnchor.constraint(equalTo: myView.bottomAnchor),
            
        ])
        
        return myView
    }
    
}

I modified your getMarker(...) func to size and position the subviews.

I also added a very similar getMarkerAutoSized(...) func that uses a couple additional constraints on the subviews.

For the first version, we must set both the Position and Size of the generated view.

For the second version, we only need to set the generated view's Position because it sizes itself to fit its subviews.

Here's how they look:

enter image description here

  • Related