Swift中的自定义视图:导致子视图边框和背景消失的约束

时间:2015-01-09 14:01:36

标签: swift interface-builder xcode6 autolayout custom-controls

我已经开始使用Swift在Xcode中创建自定义视图。我决定使用http://www.thinkandbuild.it/building-a-custom-and-designabl-control-in-swift/中显示的方法,允许我在Interface Builder中设置控件的属性。

更新:我继续构建视图,在自定义视图的子视图中排列标签,并将子视图与视图对齐。我最终在两个级别上都使用了自动布局和约束,并设法通过这种方式解决了宽度问题。我相应地更新了下面的代码。

还存在两个问题:

  1. 作为最后一步,我设置txtButton.setTranslatesAutoresizingMaskIntoConstraints(false)并设置txtButton子视图的约束=> 子视图的边框和背景消失
  2. 在IB中看不到内在大小,即我在IB 中报告布局问题,建议将高度设置为0或16。
  3. 自定义视图类:

    import UIKit
    
    @IBDesignable public class TextButtonView: UIView {
    
        @IBInspectable var borderColor: UIColor = UIColor.clearColor()
        @IBInspectable var borderWidth: CGFloat = 0
        @IBInspectable var cornerRadius: CGFloat = 0
        @IBInspectable var viewBackgroundColor: UIColor = UIColor.clearColor()
    
        @IBInspectable var mainText: String = ""
        @IBInspectable var mainTextSize: CGFloat = 15.0
        @IBInspectable var mainTextColor: UIColor = UIColor.blackColor()
    
        @IBInspectable var secText: String = ""
        @IBInspectable var secTextSize: CGFloat = 15.0
        @IBInspectable var secTextColor: UIColor = UIColor.blackColor()
    
        @IBInspectable var horizMargin: CGFloat = 5.0
        @IBInspectable var secHorizOffset: CGFloat = 0.0
        @IBInspectable var verticalMargin: CGFloat = 3.0
        @IBInspectable var lineSpacing: CGFloat = 10.0
    
    
        var txtButton: UIControl!
        var buttonHeight: CGFloat = 0.0
    
    
        #if TARGET_INTERFACE_BUILDER
        override func willMoveToSuperview(newSuperview: UIView?) {
    
            // Build the TextButton.
            txtButton = TextButton(
                borderColor: self.borderColor,
                borderWidth: self.borderWidth,
                cornerRadius: self.cornerRadius,
                viewBackgroundColor: self.viewBackgroundColor,
                mainText: self.mainText,
                mainTextSize: self.mainTextSize,
                mainTextColor: self.mainTextColor,
                secText: self.secText,
                secTextSize: self.secTextSize,
                secTextColor: self.secTextColor,
                horizMargin: self.horizMargin,
                secHorizOffset: self.secHorizOffset,
                verticalMargin: self.verticalMargin,
                lineSpacing: self.lineSpacing,
                frame: self.bounds)
    
            // Add the TextButton as subview of this view
            self.addSubview(txtButton)
    
            // Remember height for setting intrinsic content size.
            buttonHeight = txtButton.frame.size.height
    
            // Set remaining attributes for the container view.
            self.backgroundColor = UIColor.clearColor()
    
            // Setting constraints for the subview.
            txtButton.setTranslatesAutoresizingMaskIntoConstraints(false)
            self.addConstraint(NSLayoutConstraint(item: txtButton, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1, constant: 0))
            self.addConstraint(NSLayoutConstraint(item: txtButton, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0))
            self.addConstraint(NSLayoutConstraint(item: txtButton, attribute: .Right, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0))
        }
    
        #else
        override public func awakeFromNib() {
            super.awakeFromNib()
    
            // Build the TextButton.
            txtButton = TextButton(
                borderColor: self.borderColor,
                borderWidth: self.borderWidth,
                cornerRadius: self.cornerRadius,
                viewBackgroundColor: self.viewBackgroundColor,
                mainText: self.mainText,
                mainTextSize: self.mainTextSize,
                mainTextColor: self.mainTextColor,
                secText: self.secText,
                secTextSize: self.secTextSize,
                secTextColor: self.secTextColor,
                horizMargin: self.horizMargin,
                secHorizOffset: self.secHorizOffset,
                verticalMargin: self.verticalMargin,
                lineSpacing: self.lineSpacing,
                frame: self.bounds)
    
            // Add the TextButton as subview of this view.
            self.addSubview(txtButton)
    
            // Remember height for setting intrinsic content size.
            buttonHeight = txtButton.frame.size.height
    
            // Set remaining attributes for the container view.
            self.backgroundColor = UIColor.clearColor()
    
            // Setting constraints for the subview.
            txtButton.setTranslatesAutoresizingMaskIntoConstraints(false)
            self.addConstraint(NSLayoutConstraint(item: txtButton, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1, constant: 0))
            self.addConstraint(NSLayoutConstraint(item: txtButton, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0))
            self.addConstraint(NSLayoutConstraint(item: txtButton, attribute: .Right, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0))
        }
        #endif
    
        override public func intrinsicContentSize() -> CGSize {
    
            return CGSize(width: 250, height: buttonHeight)
        }
    }
    

    控制:

    import UIKit
    
    class TextButton: UIControl {
    
        // Designable properties and default values.
        var borderColor: UIColor?
        var borderWidth: CGFloat?
        var cornerRadius: CGFloat?
        var viewBackgroundColor: UIColor?
        var mainText: String?
        var mainTextSize: CGFloat?
        var mainTextColor: UIColor?
        var secText: String?
        var secTextSize: CGFloat?
        var secTextColor: UIColor?
        var horizMargin: CGFloat?
        var secHorizOffset: CGFloat?
        var verticalMargin: CGFloat?
        var lineSpacing: CGFloat?
    
        convenience init(
            borderColor: UIColor,
            borderWidth: CGFloat,
            cornerRadius: CGFloat,
            viewBackgroundColor: UIColor,
            mainText: String,
            mainTextSize: CGFloat,
            mainTextColor: UIColor,
            secText: String,
            secTextSize: CGFloat,
            secTextColor: UIColor,
            horizMargin: CGFloat,
            secHorizOffset: CGFloat,
            verticalMargin: CGFloat,
            lineSpacing: CGFloat,
            frame: CGRect) {
    
                self.init(frame: frame)
    
                self.mainText = mainText
                self.mainTextSize = mainTextSize
    
                // Button margins.
                self.horizMargin = horizMargin
                self.verticalMargin = verticalMargin
                self.secHorizOffset = secHorizOffset
                self.lineSpacing = lineSpacing
    
                // Define the Fonts
                let mainFont = UIFont(name: "Helvetica Neue", size: mainTextSize)
                let secFont = UIFont(name: "Helvetica Neue", size: secTextSize)
    
                // Create main label.
                let mainLabel: UILabel = UILabel()
                mainLabel.backgroundColor = UIColor.clearColor()
                mainLabel.textColor = mainTextColor
                mainLabel.textAlignment = .Left
                mainLabel.font = mainFont
                mainLabel.text = mainText
    
                // Calculate the main label's height.
                var mainLabelDummy: UILabel = mainLabel
                mainLabelDummy.sizeToFit()
                var mainLabelHeight: CGFloat = mainLabelDummy.frame.size.height
    
                // Create secondary label.
                let secLabel: UILabel = UILabel()
                secLabel.backgroundColor = UIColor.clearColor()
                secLabel.textColor = secTextColor
                secLabel.textAlignment = .Left
                secLabel.font = secFont
                secLabel.text = secText
    
                // Calculate the secondary label's height.
                var secLabelDummy: UILabel = secLabel
                secLabelDummy.sizeToFit()
                var secLabelHeight: CGFloat = secLabelDummy.frame.size.height
    
                // Add labels to view.
                addSubview(mainLabel)
                addSubview(secLabel)
    
                // Set constraints for labels.
                mainLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
                secLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
                self.addConstraint(NSLayoutConstraint(item: mainLabel, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1, constant: horizMargin))
                self.addConstraint(NSLayoutConstraint(item: mainLabel, attribute: .Right, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0 - horizMargin))
                self.addConstraint(NSLayoutConstraint(item: mainLabel, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: verticalMargin))
                self.addConstraint(NSLayoutConstraint(item: secLabel, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1, constant: horizMargin + secHorizOffset))
                self.addConstraint(NSLayoutConstraint(item: secLabel, attribute: .Right, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0 - horizMargin))
                self.addConstraint(NSLayoutConstraint(item: secLabel, attribute: .Top, relatedBy: .Equal, toItem: mainLabel, attribute: .Bottom, multiplier: 1, constant: lineSpacing))
    
                // Adjust frame to match content.
                self.frame.size.height =
                    2 * verticalMargin
                    + 2 * borderWidth
                    + lineSpacing
                    + mainLabelHeight
                    + secLabelHeight
    
                // Set remaining view properties.
                self.layer.borderColor = borderColor.CGColor
                self.layer.borderWidth = borderWidth
                self.layer.cornerRadius = cornerRadius
                self.backgroundColor = viewBackgroundColor
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
        }
    
        required init(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    

2 个答案:

答案 0 :(得分:0)

进一步阅读后,我提出了以下解决方案,通过删除子视图进行简化:

import UIKit

@IBDesignable class TextButtonView: UIControl {

    // Properties accessible in Interface Builder.
    @IBInspectable var borderColor: UIColor = UIColor.clearColor() {didSet { updateUI() }}
    @IBInspectable var borderWidth: CGFloat = 0 {didSet { updateUI() }}
    @IBInspectable var cornerRadius: CGFloat = 0 {didSet { updateUI() }}
    @IBInspectable var backgrColor: UIColor = UIColor.clearColor() {didSet { updateUI() }}
    @IBInspectable var mainText: String = "" {didSet { updateUI() }}
    @IBInspectable var mainTextSize: CGFloat = 20.0 {didSet { updateUI() }}
    @IBInspectable var mainTextColor: UIColor = UIColor.blackColor() {didSet { updateUI() }}
    @IBInspectable var secText: String = "" {didSet { updateUI() }}
    @IBInspectable var secTextSize: CGFloat = 12.0 {didSet { updateUI() }}
    @IBInspectable var secTextColor: UIColor = UIColor.blackColor() {didSet { updateUI() }}
    @IBInspectable var horizMargin: CGFloat = 0.0 {didSet { updateUI() }}
    @IBInspectable var secHorizOffset: CGFloat = 0.0 {didSet { updateUI() }}
    @IBInspectable var verticalMargin: CGFloat = 0.0 {didSet { updateUI() }}
    @IBInspectable var lineSpacing: CGFloat = 0.0 {didSet { updateUI() }}

    var mainLabel: UILabel!
    var secLabel: UILabel!
    var textButtonHeight: CGFloat = 0.0
    var fontName: String = "Helvetica Neue"

    required init(coder: NSCoder) {
        super.init(coder:coder)
        setupUI()
    }

    override init(frame: CGRect) {
        super.init(frame:frame)
        setupUI()
    }

    func setupUI() {

        // Set up static properties.
        mainLabel = UILabel()
        mainLabel.backgroundColor = UIColor.clearColor()
        mainLabel.textAlignment = .Left
        secLabel = UILabel()
        secLabel.backgroundColor = UIColor.clearColor()
        secLabel.textAlignment = .Left

        // Add labels to view.
        addSubview(mainLabel)
        addSubview(secLabel)

        // Update variable properties.
        updateUI()
    }

    func updateUI() {

        // Set borders and background.
        self.layer.borderColor = borderColor.CGColor
        self.layer.borderWidth = borderWidth
        self.layer.cornerRadius = cornerRadius
        self.layer.backgroundColor = backgrColor.CGColor

        // Update main label.
        mainLabel.textColor = mainTextColor
        mainLabel.font = UIFont(name: fontName, size: mainTextSize)
        mainLabel.text = mainText

        // Update secondary label.
        secLabel.textColor = secTextColor
        secLabel.font = UIFont(name: fontName, size: secTextSize)
        secLabel.text = secText

        // Calculate view's height.
        var mainLabelCopy: UILabel = mainLabel
        mainLabelCopy.sizeToFit()
        var mainLabelHeight: CGFloat = mainLabelCopy.frame.size.height
        var secLabelCopy: UILabel = secLabel
        secLabelCopy.sizeToFit()
        var secLabelHeight: CGFloat = secLabelCopy.frame.size.height
        textButtonHeight =
            2 * verticalMargin
            + 2 * borderWidth
            + lineSpacing
            + mainLabelHeight
            + secLabelHeight

        setNeedsUpdateConstraints()
    }

    override func updateConstraints() {

        // Set constraints for labels.
        setTranslatesAutoresizingMaskIntoConstraints(false)
        mainLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        secLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        removeConstraints(constraints())
        self.addConstraint(NSLayoutConstraint(item: mainLabel, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1, constant: horizMargin))
        self.addConstraint(NSLayoutConstraint(item: mainLabel, attribute: .Right, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0 - horizMargin))
        self.addConstraint(NSLayoutConstraint(item: mainLabel, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: verticalMargin))
        self.addConstraint(NSLayoutConstraint(item: secLabel, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1, constant: horizMargin + secHorizOffset))
        self.addConstraint(NSLayoutConstraint(item: secLabel, attribute: .Right, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0 - horizMargin))
        self.addConstraint(NSLayoutConstraint(item: secLabel, attribute: .Top, relatedBy: .Equal, toItem: mainLabel, attribute: .Bottom, multiplier: 1, constant: lineSpacing))
        self.addConstraint(NSLayoutConstraint(item: self, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: textButtonHeight))

        super.updateConstraints()
    }
}

在运行时,现在按预期工作。

在Interface Builder中,它仍会抛出一些关于错误放置的视图的警告,这些警告实际上没有意义。报告的实际坐标似乎不正确,IB中的手动和自动校正都没有修复它。

Warning Actual Measures

然而,这允许我继续,所以我将其作为建议的答案发布。

答案 1 :(得分:0)

我遇到了与此类似的问题,以编程方式创建约束并且子视图根本不显示。我解决这个问题的方法是将高度和宽度设置为约束。我很确定这会解决您遇到的第一个问题。