可设计的视图未在情节提要中的正确位置呈现

Designable view not rendered at correct position in Storyboard

我有一个自定义的可设计视图 class,如下所示:

@IBDesignable
class AuthInputView: UIView {
    static let nibName = "AuthInputView"
    
    @IBOutlet weak var mainContainerView: UIView!
    @IBOutlet weak var mainStackView: UIStackView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var errorLabel: UILabel!
    
    override  func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        fromNib()
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        fromNib()
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
}

和相应的名为 AuthInputView 的 nib,其文件所有者设置为 AuthInputView。

我有一个在故事板中设计的视图控制器,它有一个视图,class 设置为 AuthInputView。当我 运行 一个应用程序时,它呈现得很好,但是当我在故事板中查看它时,它看起来像这样:

Designables 也是最新的:

但是可以看出,自定义视图呈现在错误的位置(左上角)。

我用来从 nib 加载并在将 nib 的内容添加到指定视图后附加所需约束的代码如下所示:

extension UIView {
    
    @discardableResult
    func fromNib<T : UIView>() -> T? {
        guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else {
            
            return nil
        }
        self.addSubview(contentView)   
        contentView.translatesAutoresizingMaskIntoConstraints = false
       
        contentView.layoutAttachAll(to: self)
        return contentView
    }
    func layoutAttachAll(to childView:UIView)
    {
        var constraints = [NSLayoutConstraint]()
        
        childView.translatesAutoresizingMaskIntoConstraints = false
        constraints.append(NSLayoutConstraint(item: childView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0))
        constraints.append(NSLayoutConstraint(item: childView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0))
        constraints.append(NSLayoutConstraint(item: childView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0))
        constraints.append(NSLayoutConstraint(item: childView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0))
        
        childView.addConstraints(constraints)
    }
}

是什么导致故事板视图中出现这种错位?

虽然很多人喜欢使用“布局助手”功能,但很容易混淆...

您正在调用您的 layoutAttachAll 函数:

contentView.layoutAttachAll(to: self)

但是在那个函数中,你是这样做的:

func layoutAttachAll(to childView:UIView)
{
    var constraints = [NSLayoutConstraint]()
    
    constraints.append(NSLayoutConstraint(item: childView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0))
    ...

但您已将 self 传递为 childView,因此您将 self 限制为 self

如果您将约束代码“内联”:

func fromNib<T : UIView>() -> T? {
    guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else {
        return nil
    }
    self.addSubview(contentView)

    var constraints = [NSLayoutConstraint]()
    
    contentView.translatesAutoresizingMaskIntoConstraints = false
    
    constraints.append(NSLayoutConstraint(item: contentView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0))
    constraints.append(NSLayoutConstraint(item: contentView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0))
    constraints.append(NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0))
    constraints.append(NSLayoutConstraint(item: contentView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0))
    
    self.addConstraints(constraints)
    
    return contentView
}

您应该不会再看到“放错地方”的视图。

如果你真的想使用你的 layoutAttachAll 函数,你想调用它:

self.layoutAttachAll(to: contentView)

并更改最后一行:

    // adding to wrong view
    //childView.addConstraints(constraints)
    self.addConstraints(constraints)

也许值得注意的是,您可以将“助手”扩展大大简化为:

extension UIView {
    @discardableResult
    func fromNib<T : UIView>() -> T? {
        guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else {
            return nil
        }
        self.addSubview(contentView)

        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.frame = bounds
        
        return contentView
    }
}