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: