【问题标题】:IBDesignable - Arrange subviews added through interface builderIBDesignable - 排列通过界面生成器添加的子视图
【发布时间】:2016-04-28 19:11:35
【问题描述】:

我目前正在处理IBDesignable Views,我很好奇是否有人能够解决这个问题。我希望在我的子视图中使用自定义布局算法自动排列通过界面构建​​器添加的视图。当我运行应用程序时,视图效果很好,但在界面构建器中,视图不会实时重新排列。

我已经尝试调试我的UIView 类,但是界面构建器似乎总是在初始化元素时,它认为它的子视图为零。界面构建器似乎没有给您机会在事后安排这些视图。但是,我想知道是否可能只是我缺少一些东西。是否可以在 IBDesignable 类中重新排列从界面构建器添加的子视图,并在界面构建器中重新排列视图?

【问题讨论】:

    标签: ios interface-builder ibdesignable


    【解决方案1】:

    如果您还没有使用自定义视图和 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()
    }
      }
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-05
        • 2013-07-25
        • 1970-01-01
        • 2012-02-09
        • 2021-06-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多