IB — 根据条件初始化特定的自定义 UIView

IB — Initialize specific custom UIView depending on conditions

我有以下 类:

/**
 * This is basically an empty class and exists for the purpose of being able to 
 * define IBOutlets as BaseHeading, regardless of which specific heading is used in IB.
 */
class BaseHeading: UIView { }


/**
 * This class has a .xib where the layout is defined.
 * This view should be used as navigationBar for modally-presented ViewControllers.
 */
class HeadingIconRight: BaseHeading { } // This class has a .xib. 

/**
 * This class has a .xib where the layout is defined.
 * This view should be used as navigationBar for pused ViewControllers.
 */
class HeadingIconLeft: BaseHeading { } // This class has a .xib.

MyViewController 的 .xib 中,我有一个类型为 BaseHeading 的自定义 UIView。在它的 Swift-file 中,我有一个 @IBOutlet private weak var heading: BaseHeading!,与之相连。

在 Swift 文件中我知道应该使用哪个特定的 header (HeadingIconRight / HeadingIconLeft),我只是不确定如何在代码中实现这一点.

我想要的:

到目前为止我尝试过的:

let headingIndex = self.view.subviews.firstIndex { [=11=] == self.heading }
let newHeading   = HeadingIconRight()

// Loop through 'old' heading's constraints and convert them to the new heading.
self.heading.constraints.forEach { constraint in
    
    let firstItem:  AnyObject? = constraint.firstItem as! AnyHashable  == self.heading as AnyHashable ? newHeading : constraint.firstItem
    let secondItem: AnyObject? = constraint.secondItem as! AnyHashable == self.heading as AnyHashable ? newHeading : constraint.secondItem
    
    newHeading.addConstraint(
        NSLayoutConstraint(item: firstItem,
                           attribute: constraint.firstAttribute,
                           relatedBy: constraint.relation,
                           toItem: secondItem,
                           attribute: constraint.secondAttribute,
                           multiplier: constraint.multiplier,
                           constant: constraint.constant)
    )
}

self.heading.removeFromSuperview()
self.view.insertSubview(newHeading, at: headingIndex!)
self.heading = newHeading

当然,我会尝试更多,但我不想构建非常复杂的东西,而是希望在正确的方向上得到一些指导,以找到一个好的解决方案。

如果能以某种方式覆盖初始化 Xib 视图的生命周期函数,并用特定的 header 替换这个函数,那将是一个很好的解决方案(如果它不违反UIViewController API).

我选择了 @Paulw11's 并在运行时用代码创建了视图。

我没有在 IB 中添加 BaseHeading-视图,而是将下一个最顶层视图限制在 ViewController 的顶部并引用此约束(例如 topMostConstraint).

我在 BaseHeading 中添加了以下函数:

public func constrainToTop(in container: UIView, above otherView: UIView, bottomMargin: CGFloat = 0, disableConstraint: NSLayoutConstraint? = nil)
{
    container.addSubview(self)
    
    self.translatesAutoresizingMaskIntoConstraints = false
    
    self.topAnchor.constraint(equalTo: container.topAnchor).isActive                             = true
    self.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive                     = true
    self.trailingAnchor.constraint(equalTo: container.trailingAnchor).isActive                   = true
    self.bottomAnchor.constraint(equalTo: otherView.topAnchor, constant: -bottomMargin).isActive = true
    
    disableConstraint?.isActive = false
}

这是做什么的:

  • 添加self到containerView
  • 创建必要的约束条件
  • 如果需要,将 bottomMargin 添加到下一个(下方)视图
  • 禁用将另一个视图限制在屏幕顶部的旧约束

在我的 ViewControllerviewDidLoad() 中,我现在可以执行以下操作:

public class MyViewController: UIViewController
{
    private var heading: BaseHeading?

    @IBOutlet private weak var topMostConstraint: NSLayoutConstraint!
    @IBOutlet private weak var anyOtherView:      UIStackView!

    override func viewDidLoad()
    {
        super.viewDidLoad()
    
        self.heading = HeadingIconRight() // Or any other BaseHeading-subclass.
        self.heading?.constrainToTop(in: self.view,
                                     above: self.anyOtherView,
                                     bottomMargin: 16,
                                     disableConstraint: self.topMostConstraint)
    }
}

这似乎可以完成工作,而且比我预期的要简单。