【问题标题】:Preventing Auto Layout from stretching custom view防止自动布局拉伸自定义视图
【发布时间】:2019-03-26 02:22:47
【问题描述】:

我在 swift 中创建了一个自定义视图,并试图让它正确显示。它本质上是一张素材卡片,可以通过按更多按钮来扩展视图。指定底部约束时出现了我的问题。它是必需的,但设置它会扩展我的自定义视图。

我已经在 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)

        ])
    }
}

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

leftright

我对 iOS 比较陌生,但对 android 有经验,所以任何与此相关的解释都会非常有帮助。

谢谢。

【问题讨论】:

    标签: ios swift autolayout


    【解决方案1】:

    所以我找到了一个似乎可以满足我需求的解决方案。我仍在努力思考我真正想要实现的目标。基本上我想要的是我的视图高度由其子视图及其约束来定义,而不是必须在约束中指定它。同时我需要满足界面中的高度限制。

    我的解决方案如下:在我的界面中的卡片视图中添加一个低优先级高度约束为0。这满足了场景中对高度的要求,同时也让我的视野在不被拉伸的情况下扩展和收缩。

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

    我在another stack overflow question找到了这个解决方案。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

        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
        }
        

        我不能评论你的帖子,因为没有足够的声誉:)

        【讨论】:

        • 感谢您的回答,尽管我的问题措辞不佳。假设我知道视图的大小,这个解决方案可以正常工作,但由于数据是动态的,我不想提供大小。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-17
        相关资源
        最近更新 更多