IBDesignable - 通过接口构建器排列添加的子视图

3

我目前正在使用 IBDesignable 视图进行操作,我很好奇是否有人已经解决了这个问题。 我想让通过接口构建器添加的视图在我的子视图中自动排列使用自定义布局算法。 当我运行应用程序时,该视图效果非常好,但是在接口构建器中,视图不会实时重新排列。

我尝试调试我的 UIView 类,但似乎在接口构建器初始化元素时,它始终认为它没有子视图。 似乎接口构建器不允许您事后安排这些视图。 但是,我想知道是否有什么我错过了。 是否可以在 IBDesignable 类中重新排列从接口构建器添加的子视图,并使视图在接口构建器中显示为重排列?

2个回答

0

似乎没有任何方法可以做到这一点。如果您将自定义控件拖放到另一个xib中,并在Interface Builder中向自定义控件添加子视图,则当您加载xib时,这些子视图将与自定义控件在视图层次结构中处于相同级别。看起来自定义控件无法在其他xib中充当容器。


0

如果您还没有使用提供的自定义视图和IBDesignable方法,请尝试使用它们。您可能还需要在Xcode中刷新视图或自动刷新视图。以下是您可能缺少的函数。此函数在实际应用程序中永远不会被调用。它只在Xcode IB中调用。

override func prepareForInterfaceBuilder() {
    super.prepareForInterfaceBuilder()
    setUpView() 
}

在这个例子中,setUpView正在布局我的子视图。
这是我制作的一个示例。https://github.com/agibson73/ICONButton
import UIKit

@IBDesignable class AGIconButton: UIControl {

private var iconImageView : UIImageView!
private var iconLabel : UILabel!
private var mainSpacer : UIView!
private var highlightView:UIView!
private var widthContraint : NSLayoutConstraint!
var padding : CGFloat = 5


override init(frame: CGRect) {
    super.init(frame: frame)
    setUpView()
    addTarget(self, action: #selector(AGIconButton.userDidTouchDown), for: .touchDown)
    addTarget(self, action: #selector(AGIconButton.userDidTouchUp), for: .touchUpInside)
    addTarget(self, action: #selector(AGIconButton.userDidTouchUpOutside), for: .touchUpOutside)

}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setUpView()
    addTarget(self, action: #selector(AGIconButton.userDidTouchDown), for: .touchDown)
    addTarget(self, action: #selector(AGIconButton.userDidTouchUp), for: .touchUpInside)
    addTarget(self, action: #selector(AGIconButton.userDidTouchUpOutside), for: .touchUpOutside)


}

//only called at design time
override func prepareForInterfaceBuilder() {
    super.prepareForInterfaceBuilder()
    setUpView()
    addTarget(self, action: #selector(AGIconButton.userDidTouchDown), for: .touchDown)
    addTarget(self, action: #selector(AGIconButton.userDidTouchUp), for: .touchUpInside)
    addTarget(self, action: #selector(AGIconButton.userDidTouchUpOutside), for: .touchUpOutside)

}




@IBInspectable var iconImage: UIImage = UIImage() {
    didSet {
        iconImageView.image = iconImage
    }
}


@IBInspectable var imageSize: CGFloat = 40 {
    didSet {
         setUpView()
    }
}


@IBInspectable var imagePadding: CGFloat = 10 {
    didSet {
           setUpView()
    }
}

@IBInspectable var iconText: String = "Icon Button Time" {
    didSet {
          setUpView()
    }
}

@IBInspectable var iconTextSize: CGFloat = 15 {
    didSet {
          setUpView()
    }
}

@IBInspectable var iconTextColor: UIColor = UIColor.black {
    didSet {
        setUpView()
    }
}

@IBInspectable var alignment: Int = 1 {
    didSet {
        setUpView()
    }
}


override var intrinsicContentSize: CGSize {
    let label = UILabel()
    label.font = UIFont.systemFont(ofSize: iconTextSize)
    label.text = iconText
    label.sizeToFit()
    return CGSize(width: imageSize + label.frame.width + imagePadding + (padding * 2), height: CGFloat(max(label.frame.height, imageSize) + padding * 2))
}


@IBInspectable var highLightColor: UIColor = UIColor.lightGray {
    didSet {
         setUpView()
    }
}

@IBInspectable var shouldBounce: Bool = true

@IBInspectable var borderColor: UIColor = UIColor.clear {
    didSet {
        layer.borderColor = borderColor.cgColor
    }
}

@IBInspectable var borderWidth: CGFloat = 0 {
    didSet {
        layer.borderWidth = borderWidth
    }
}

@IBInspectable var cornerRadius: CGFloat = 0 {
    didSet {
        layer.cornerRadius = cornerRadius
    }
}

private func setUpView(){

    if iconImageView == nil{
        iconImageView = UIImageView(image: iconImage)
        iconImageView.contentMode = .scaleAspectFit
        iconImageView.isUserInteractionEnabled = false
        self.addSubview(iconImageView)

    }

    if mainSpacer == nil{
        mainSpacer = UIView(frame: CGRect(x: 0, y: 0, width: imagePadding, height: self.bounds.height))
        mainSpacer.isUserInteractionEnabled = false
        self.addSubview(mainSpacer)
    }



    if iconLabel == nil{
        iconLabel = UILabel()
        iconLabel.isUserInteractionEnabled = false
        self.addSubview(iconLabel)

    }

    if highlightView == nil{
        highlightView = UIView(frame: self.bounds)
        highlightView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
        highlightView.alpha = 0
        highlightView.isUserInteractionEnabled = false
        self.addSubview(highlightView)
        self.bringSubview(toFront: highlightView)
    }
    highlightView.backgroundColor = highLightColor

    iconLabel.font = UIFont.systemFont(ofSize: iconTextSize)
    iconLabel.text = iconText
    iconLabel.textColor = iconTextColor
    iconLabel.sizeToFit()


    var usedWidth : CGFloat = self.intrinsicContentSize.width

    if bounds.width < usedWidth{
        usedWidth = bounds.width
    }

    let maxImageHeight = min(self.bounds.height - padding, imageSize)

    //resize iconlabel if we have to
    if maxImageHeight + imagePadding + iconLabel.bounds.width + padding * 2 > usedWidth{
        iconLabel.frame = CGRect(x: 0, y: 0, width: self.bounds.width - iconImageView.bounds.width - imagePadding - padding * 2, height: iconLabel.bounds.height)
        iconLabel.fitFontForSize(minFontSize: 1, maxFontSize: iconTextSize, accuracy: 1.0)

    }

    let maxWidth = (self.bounds.width - iconLabel.bounds.width - maxImageHeight - imagePadding) / 2

    switch alignment {
    case 0:
        //intrinsic left
        iconImageView.frame = CGRect(x:padding, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
        mainSpacer.frame = CGRect(x: maxImageHeight + padding, y: 0, width: imagePadding, height: self.bounds.height)
        iconLabel.frame = CGRect(x: maxImageHeight + imagePadding + padding, y: 0, width: iconLabel.frame.width, height: bounds.height)

        break
    case 1:
        //intrinsic center
        iconImageView.frame = CGRect(x: maxWidth, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
        mainSpacer.frame = CGRect(x: maxWidth + maxImageHeight, y: 0, width: imagePadding, height: self.bounds.height)
        iconLabel.frame = CGRect(x: maxWidth + maxImageHeight + imagePadding, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
        break
    case 2:
        //intrinsic icon right text aligned right
        iconLabel.frame = CGRect(x: maxWidth, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
        iconLabel.textAlignment = .right
        mainSpacer.frame = CGRect(x: iconLabel.frame.width + maxWidth, y: 0, width: imagePadding, height: self.bounds.height)
        iconImageView.frame = CGRect(x: iconLabel.frame.width + imagePadding + maxWidth, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
        break
    case 3:
        //intrinsic center invert icon
        iconLabel.frame = CGRect(x:maxWidth, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
        mainSpacer.frame = CGRect(x: maxWidth + iconLabel.bounds.width, y: 0, width: imagePadding, height: self.bounds.height)
        iconImageView.frame = CGRect(x: maxWidth + iconLabel.bounds.width + imagePadding, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)

        break

    default:
        //intrinsic center
        iconImageView.frame = CGRect(x: maxWidth, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
        mainSpacer.frame = CGRect(x: maxWidth + maxImageHeight, y: 0, width: imagePadding, height: self.bounds.height)
        iconLabel.frame = CGRect(x: maxWidth + maxImageHeight + imagePadding, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
    }


}


//layout subviews
override func layoutSubviews() {
    super.layoutSubviews()
    setUpView()
}


//MARK: Touch Events
//TODO: run on timer to simulate a real press
func userDidTouchDown(){
    if shouldBounce == true{
        animateBouncyDown()
    }else{
        self.animateHighlightTo(alpha: 0.3)
    }

}


func userDidTouchUp(){
    if shouldBounce == true{
        animateBouncyUp()
    }else{
        self.animateHighlightTo(alpha: 0)
    }
}

func userDidTouchUpOutside(){
    if shouldBounce == true{
        animateBouncyUp()
    }else{
        self.animateHighlightTo(alpha: 0)
    }
}

func animateHighlightTo(alpha:CGFloat){
    UIView.animate(withDuration: 0.2, animations: {  [weak self] in
        self?.highlightView.alpha = alpha
    })

}

func animateBouncyDown(){
    self.transform = CGAffineTransform.identity
    UIView.animate(withDuration: 0.15, animations: { [weak self] in
        self?.transform = CGAffineTransform(scaleX: 0.85, y: 0.85)
    })
}

func animateBouncyUp(){
    UIView.animate(withDuration: 0.2, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: .curveEaseInOut, animations: {[weak self] in
        if self != nil{
            self?.transform = CGAffineTransform.identity
        }
    }, completion: nil)
}
}

extension UILabel {

func fitFontForSize( minFontSize : CGFloat = 1.0, maxFontSize : CGFloat = 300.0, accuracy : CGFloat = 1.0) {
    var maxFontSize = maxFontSize
    var minFontSize = minFontSize
    assert(maxFontSize > minFontSize)
    layoutIfNeeded() // Can be removed at your own discretion
    let constrainedSize = bounds.size
    while maxFontSize - minFontSize > accuracy {
        let midFontSize : CGFloat = ((minFontSize + maxFontSize) / 2)
        font = font.withSize(midFontSize)
        sizeToFit()
        let checkSize : CGSize = bounds.size
        if  checkSize.height < constrainedSize.height && checkSize.width < constrainedSize.width {
            minFontSize = midFontSize
        } else {
            maxFontSize = midFontSize
        }
    }
    font = font.withSize(minFontSize)
    sizeToFit()
    layoutIfNeeded()
}
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接