I’m trying to make ui using snapkit and practice MVVM pattern, but when I try to add stackview I keep getting an error that says
‘Unable to activate constraint with anchors <NSLayoutYAxisAnchor:0x600001753fc0 “UIStackView:0x10380aba0.centerY”> and <NSLayoutYAxisAnchor:0x600001753a00 “UIImageView:0x103905b00.centerY”> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That’s illegal.’
When I comment out the stackview code line, everything works well, so I guess there is something wrong with the stackview.
This is Post.swift code.
struct Post {
let userHandle: String
let userProfileImage: String
let location: String
let selfieImage: String
let rearViewImage: String
let caption: String
init(userHandle: String, userProfileImage: String, location: String, selfieImage: String, rearViewImage: String, caption: String) {
self.userHandle = userHandle
self.userProfileImage = userProfileImage
self.location = location
self.selfieImage = selfieImage
self.rearViewImage = rearViewImage
self.caption = caption
}
}
This is PostView.swift code.
import UIKit
import SnapKit
class PostView: UIView {
var userProfileImageName: String = "" {
willSet {
userProfileImageView.image = UIImage(named: newValue)
print("userProfileImage: \(newValue)")
}
}
var selfieImageName: String = "" {
willSet {
selfieImageView.image = UIImage(named: newValue)
print("selfieImage: \(newValue)")
}
}
var rearImageName: String = "" {
willSet {
rearViewImageView.image = UIImage(named: newValue)
print("rearImage: \(newValue)")
}
}
let userProfileImageView: UIImageView = {
let imageView = UIImageView()
imageView.clipsToBounds = true
return imageView
}()
lazy var userHandleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .natural
label.textColor = .white
label.font = .systemFont(ofSize: 13, weight: .semibold)
label.backgroundColor = .lightGray
return label
}()
lazy var locationLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 10)
label.textColor = .lightGray
label.backgroundColor = .black
return label
}()
var stackView: UIStackView {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 5
return stackView
}
let selfieImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 10.0
return imageView
}()
let rearViewImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 10.0
return imageView
}()
let captionLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 15.0, weight: .semibold)
label.textColor = .white
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .darkGray
addSubview(userProfileImageView)
addSubview(stackView)
stackView.addArrangedSubview(userHandleLabel)
stackView.addArrangedSubview(locationLabel)
addSubview(rearViewImageView)
addSubview(captionLabel)
// constraint
userProfileImageView.snp.makeConstraints { make in
make.top.equalToSuperview().inset(5)
make.leading.equalToSuperview().inset(5)
make.width.equalTo(50)
make.height.equalTo(userProfileImageView.snp.width)
}
stackView.snp.makeConstraints { make in
make.centerY.equalTo(userProfileImageView.snp.centerY)
make.leading.equalTo(userProfileImageView.snp.trailing).offset(5)
}
rearViewImageView.snp.makeConstraints { make inmake.top.equalTo(userProfileImageView.snp.bottom).offset(10)
make.leading.trailing.equalToSuperview()
// height == width * 4/3
make.height.equalTo(rearViewImageView.snp.width).multipliedBy(4.0 / 3.0)
// don't need this
//make.centerX.equalToSuperview()
}
captionLabel.snp.makeConstraints { make in
make.top.equalTo(rearViewImageView.snp.bottom).offset(5)
make.leading.equalTo(rearViewImageView.snp.leading)
make.trailing.equalTo(rearViewImageView.snp.trailing)
make.bottom.equalToSuperview().inset(10)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
This is PostViewModel.swift code.
class PostViewModel {
private let post: Post
init(post: Post) {
self.post = post
}
var userHandle: String {
return post.userHandle
}
var userProfileImage: String {
return post.userProfileImage
}
var location: String {
return post.location
}
var selfieImage: String{
return post.selfieImage
}
var rearViewImage: String {
return post.rearViewImage
}
var caption: String {
return post.caption
}
}
extension PostViewModel {
func configure(_ view: PostView){
view.locationLabel.text = location
view.captionLabel.text = caption
view.rearImageName = rearViewImage
view.selfieImageName = selfieImage
view.userProfileImageName = userProfileImage
view.userProfileImageView.layer.cornerRadius = 25
print("== \(view.userProfileImageView.layer.cornerRadius)")
}
}
This is the viewcontroller.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let newPost = Post(userHandle: "userHandle", userProfileImage: "profileImage.png", location: "A location", selfieImage: "selfieImage.jpg", rearViewImage: "rearImage.jpg", caption: "BeReal")
//let postView = PostView()
let postViewModel = PostViewModel(post: newPost)
let postView = PostView()
postViewModel.configure(postView)
view.addSubview(postView)
postView.snp.makeConstraints { make in
//make.centerY.equalToSuperview()
make.leading.trailing.equalToSuperview()
}
postView.backgroundColor = .lightGray
}
}
I also tried making the stackview’s constraint with equalToSuperview(), but I get Fatal error: Expected superview but found nil when attempting make constraint
equalToSuperview.
error, so I guess stackview is not being added…?
I also double-checked that I’m adding subview before making constraints. I just can’t figure out what causes this error.
1. Don’t use the same view when setting constraint to the same view, in your case “userProfileImageView” and “rearViewImageView” 2. remove this line “make.width.equalTo(userProfileImageView.snp.height).multipliedBy(1.0)” and set a height constraint for that view(userProfileImageView) 3. stackView is not having height and width constraint