防止自动版面扩展自定义视图

时间:2019-03-26 02:22:47

标签: ios swift autolayout

我已经快速创建了一个自定义视图,并试图使其正确显示。它本质上是材料卡,可以通过按“更多”按钮来扩展视图。指定底部约束时出现我的问题。它是必需的,但设置它可以扩展我的自定义视图。

我已经在android中实现了它,我想我正在寻找android:height='wrap_content'的类似物。我尝试设置纵横比约束,以使视图保持适当的大小,但阻止自定义视图在其子视图更改时扩展。另外,我尝试使用lessThanOrEqualTo约束,但这太模糊了,无法满足底部约束。

这是我的UIExpandableCard视图的样子:

import Foundation
import MaterialComponents

@IBDesignable
public class UIExpandableCard: UIView {

    // attributes
    @IBInspectable var overlineText: String? {
        didSet {
            overlineLabel.text = overlineText?.uppercased()
        }
    }

    @IBInspectable var headlineText: String? {
        didSet {
            headlineLabel.text = headlineText
        }
    }

    @IBInspectable var bodyText: String? {
        didSet {
            bodyLabel.text = bodyText
        }
    }

    @IBInspectable var logoImage: UIImage? {
        didSet {
            logoImageView.image = logoImage
        }
    }

    var cardView: MDCCard!
    var overlineLabel: UILabel!
    var headlineLabel: UILabel!
    var bodyLabel: UILabel!
    var moreButton: UIButton!
    var logoImageView: UIImageView!

    var isCardExpanded = false


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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    private func setup() {
        self.translatesAutoresizingMaskIntoConstraints = false
        setupViews()
        setupConstraints()
    }

    private func setupViews() {
        self.clipsToBounds = true
        cardView = MDCCard(frame: CGRect.zero)
        cardView.translatesAutoresizingMaskIntoConstraints = false
        cardView.isInteractable = false
        self.addSubview(cardView)


        overlineLabel = UILabel(frame: CGRect.zero)
        overlineLabel.translatesAutoresizingMaskIntoConstraints = false
        overlineLabel.font = UIFont.preferredFont(forTextStyle: .footnote)

        overlineLabel.text = overlineText?.uppercased()
        self.addSubview(overlineLabel)

        headlineLabel = UILabel(frame: CGRect.zero)
        headlineLabel.translatesAutoresizingMaskIntoConstraints = false
        headlineLabel.font = UIFont.preferredFont(forTextStyle: .title1)
        headlineLabel.text = headlineText
        self.addSubview(headlineLabel)

        bodyLabel = UILabel(frame: CGRect.zero)
        bodyLabel.translatesAutoresizingMaskIntoConstraints = false
        bodyLabel.font = UIFont.preferredFont(forTextStyle: .body)
        bodyLabel.numberOfLines = 1
        bodyLabel.text = bodyText
        self.addSubview(bodyLabel)

        logoImageView = UIImageView(image: logoImage)
        logoImageView.translatesAutoresizingMaskIntoConstraints = false
        logoImageView.contentMode = .scaleAspectFit
        self.addSubview(logoImageView)

        moreButton = UIButton(type: .roundedRect)
        moreButton.isUserInteractionEnabled = true
        moreButton.translatesAutoresizingMaskIntoConstraints = false

        moreButton.setTitle("More", for: .normal)
        moreButton.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)

        self.addSubview(moreButton)

    }

    @objc func buttonClicked(_ sender: UIButton) {
        if !isCardExpanded {
            moreButton.setTitle("Less", for: .normal)
            bodyLabel.numberOfLines =  0
        } else {
            moreButton.setTitle("More", for: .normal)
            bodyLabel.numberOfLines = 1
        }

        isCardExpanded = !isCardExpanded
    }

    private func setupConstraints() {
        NSLayoutConstraint.activate([
            cardView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 8),
            cardView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -8),
            cardView.topAnchor.constraint(equalTo: self.topAnchor, constant: 8),
            cardView.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor, constant: -8),

            overlineLabel.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 16),
            overlineLabel.topAnchor.constraint(equalTo: cardView.topAnchor, constant: 16),

            headlineLabel.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 16),
            headlineLabel.topAnchor.constraint(equalTo: overlineLabel.bottomAnchor, constant: 8),


            NSLayoutConstraint(item: logoImageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 48),
            NSLayoutConstraint(item: logoImageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 48),
            logoImageView.trailingAnchor.constraint(equalTo: cardView.trailingAnchor, constant: -16),
            logoImageView.topAnchor.constraint(equalTo: cardView.topAnchor, constant: 16),


            bodyLabel.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 16),
            bodyLabel.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 16),
            bodyLabel.trailingAnchor.constraint(equalTo: cardView.trailingAnchor, constant: -8),

            moreButton.topAnchor.constraint(greaterThanOrEqualTo: bodyLabel.bottomAnchor, constant: 8),
            moreButton.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 16),
            moreButton.bottomAnchor.constraint(equalTo: cardView.bottomAnchor, constant: -8)

        ])
    }
}

基本上我想要类似左边的东西。但是,我做对了,其中一个视图(蓝色)正在拉伸以填充约束。

left right

我是iOS的新手,但是有使用android的经验,因此任何与此相关的解释都将有所帮助。

谢谢。

3 个答案:

答案 0 :(得分:0)

您无法使用lessThanOrEqualTo来做到这一点。尽管我无法完全理解您的问题,但希望这种方法能有所帮助。

首先定义流约束,例如:

var heightConstraint: NSLayoutConstraint?

并在setupConstraint中将其替换为底部约束:

private func setupConstraints() {

        heightConstraint = cardView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 1, constant: 0)
        NSLayoutConstraint.activate([
            cardView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 8),
            cardView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -8),
            cardView.topAnchor.constraint(equalTo: self.topAnchor, constant: 8),
            heightConstraint,

            overlineLabel.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 16),
            overlineLabel.topAnchor.constraint(equalTo: cardView.topAnchor, constant: 16),

.
.
.

最后按下按钮时:

private func expandViewWithAnimation(_ isExpand: Bool) {
    UIView.animate(withDuration: 0.8) {
            self.heightConstraint?.constant = isExpand ? 80:0
            self.layoutIfNeeded()
        }
}

@objc func buttonClicked(_ sender: UIButton) {
        if !isCardExpanded {
            moreButton.setTitle("Less", for: .normal)
            bodyLabel.numberOfLines =  0
        } else {
            moreButton.setTitle("More", for: .normal)
            bodyLabel.numberOfLines = 1
        }

        expandViewWithAnimation(isCardExpanded)
        isCardExpanded = !isCardExpanded
}

由于信誉不足,我无法对您的信息发表评论:)

答案 1 :(得分:0)

因此,我找到了一种解决方案,该解决方案似乎可以满足我的需求。我仍在努力将自己实际想达到的目标包围住。基本上,我想要的是视图高度由其子视图及其约束定义,而不必在约束中指定。同时,我需要满足界面中的高度限制。

我的解决方案如下:在我的界面中,向卡片视图添加低优先级高度限制0。这满足了对场景高度的要求,同时还允许我的视图在不拉伸的情况下展开和收缩。

  

...整个视图的高度为零的低优先级约束。低优先级约束将尝试缩小程序集,而其他约束则阻止其缩小到足以裁剪其子视图的程度。

我在another stack overflow question上找到了此解决方案。

答案 2 :(得分:-1)

Joshua's answer之后,我阻止了自定义视图的拉伸:

let width = widthAnchor.constraint(equalToConstant: 0)
width.priority = .defaultLow
width.isActive = true